IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> <C++>C++入门——缺省参数、函数重载 -> 正文阅读

[C++知识库]<C++>C++入门——缺省参数、函数重载

在这里插入图片描述

1. 缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。

这里有个例子:车子有个备胎,当车轮坏掉时才会启用备胎,如果有车轮就用车轮。

翻译一下:函数有缺省参数,当函数没有指定实参就用缺省值,如果有实参就用实参。(额,如果还不太好理解就请联想一下另一种“备胎”😢);

1.1 缺省参数的分类

#include<iostream>
using namespace std;
//全缺省(全都缺省)
void testA(int a = 1, int b = 2, int c = 3)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}
//半缺省(缺省一部分)
//必须从右往左给缺省值,并且是连续的。否则会产生歧义,不知道你传的参是给谁传的
void testB(int a, int b = 2, int c = 3)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}

int main()
{
	//全缺省
	testA();//1 2 3
	testA(10);//10 2 3
	testA(10, 20);//10 20 3
	testA(10, 20, 30);//10 20 30
	//如果我只想传第二个位置的实参,这样写可以吗?不行哦,C++暂时不支持这样的语法
	//testA(, 20);//错误


	//半缺省
	//至少得传一个参
	testB(10);//10 2 3
	testB(10, 20);//10 20 3
	testB(10, 20, 30);//10 20 30
	return 0;
}

1.2 缺省参数的应用

栈的初始化

struct Stack
{
	int* a;
	int size;
	int capacity;
};

//半缺省
//在初始化时默认n为4,如果有其他需求也可以改变n的值
void StackInit(struct Stack* ps, int n = 4)
{
	assert(ps);
	ps->a = (int*)malloc(sizeof(int) * n);
	ps->size = 0;
	ps->capacity = n;
}

int main()
{
	struct Stack st;
	StackInit(&st, 100);
	return 0;
}

1.3 缺省参数的注意点

1?? 缺省值必须是常量或者全局变量

2?? C语言不支持(编译器不支持 )

3????注意!:缺省参数不能在函数声明和定义中同时出现

//a.h
void TestFunc(int a = 10);
// a.c
void TestFunc(int a = 20)
{}
// 注意:如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用哪个缺省值。

只用在声明给缺省值,定义里不用给。

在这稍微解释一下:因为编译阶段我们包含的头文件已经展开了,我们拥有了声明(但还没拿到定义),这时如果我们没有传参,编译器就要去找缺省值,就成功在声明中找到了。如果只把缺省值放在定义里,编译阶段还拿不到定义,就找不到缺省值,会报错!(汇编阶段生成符号表才能找到定义)

image-20220503180404723

2.函数重载

2.1 函数重载概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的
形参列表(参数个数 或 类型 或 顺序)必须有不同,常用来处理实现功能类似数据类型不同的问题

//参数个数不同
double Add(int left, double mid, double right)
{
	return left + mid + right;
}
//类型的顺序不同
double Add(int left, double right)
{
	return left + right;
}

double Add(double left, int right)
{
	return left + right;
}

int main()
{
	cout << Add(1, 2.2, 3.3) << endl;
	cout << Add(1, 2.2) << endl;
	cout << Add(1.1, 2) << endl;
	return 0;
}

下面两个函数属于函数重载吗?

short Add(short left, short right)
{
	return left+right;
}
int Add(short left, short right)
{
	return left+right;
}

上面两个函数只有返回值不同,不构成函数重载。因为调用的时候没法判断要调用哪个。

2.2 C++支持函数重载的底层逻辑

为什么C++支持函数重载,而C语言不支持函数重载呢?
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。

image-20220503180404723

1??实际我们的项目通常是由多个头文件和多个源文件构成,而通过我们C语言阶段学习的编译链接,我们可以知道,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?
2??所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。如果啥都不记得了请看👉c语言自学教程——程序环境和预处理
3??那么链接时,面对Add函数,连接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。
由于Windows下vs的修饰规则过于复杂,而Linux下gcc的修饰规则简单易懂,下面我们使用了gcc演示了这个修饰后的名字。
5??通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。

image-20220505181556582

6??通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
7??另外我们也理解了,为什么函数重载要求参数不同!而跟返回值没关系

结论:

  • 在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变

  • 在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中

2.3 extern“C”

有时候在C++工程中可能需要将某些函数按照C的风格来编译**,**在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。实际上C和C++都可以相互调用(C->C、C->C++、C++ ->C++、C++ ->C)

比如:tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree两个接口来使用,但如果是C项目就没办法使用,那么就使用extern “C”来解决。

鉴于大家都是初学者,我们就来手把手教学如何调用静态库

假设我们想调用栈,首先将栈封装成静态库:

image-20220506170836592

完成后重新生成解决方案,生成Stack_C.lib文件

image-20220506172832669

在Stack_C\Debug文件下能找到静态库

image-20220506173017699

之后我们创建一个cpp文件,通过静态库使用栈

image-20220506174611891

#include"../../Stack_C/Stack_C/Stack.h"//包含头文件

image-20220506175633634

image-20220506175824371

找到你的静态库生成的地址,我的是Stack_C\Debug,找到之后点确认

image-20220506181957114

两个静态库之间记得要加

但是你这样运行还是会有链接错误

现在把栈的.c文件改成.cpp文件,在运行就可以通过啦!(就相当于cpp调用cpp

之前链接不上的原因是:test文件是cpp文件,stack是c文件,cpp通过符号表找不到c的函数(上面解释过:两者的函数名修饰规则不同)

那么我们能否让cpp文件调用c呢?加上个extern"C",告诉编译器按照C语言风格找函数名。

extern"C"
{
	#include"../../Stack_C/Stack_C/Stack.h"//包含头文件
}

开始尝试c调用c再搞一个test.c文件设置好C的附加库目录就能成功执行了

重点是如何c调用cpp

跟上面的操作一样,再创建一个文件叫Stack_CPP,封装成CPP的静态库,让test.c文件设置好CPP的附加库目录、调用.h文件

想要跳转到上面再操作一次请点击这句话

执行之后仍然会报错(因为命名规则不同,找不到)

这时,我们在cpp文件里用上条件编译,当在cpp文件中EXTERN_C 被识别成extern “C”,在c文件里隐藏EXTERN_C(因为.c文件不认识extern “C”)这样让cpp的名字修饰规则跟c一样,就能链接起来了

#ifdef __cplusplus
	#define EXTERN_C extern "C"
#else
	#define EXTERN_C
#endif

EXTERN_C void StackInit(ST* ps);//初始化
EXTERN_C void StackDestory(ST* ps);//销毁
EXTERN_C void StackPush(ST* ps, STDataType x);//入栈
EXTERN_C void StackPop(ST* ps);//出栈
EXTERN_C bool StackEmpty(ST* ps);//判断栈是否为空
EXTERN_C STDataType StackTop(ST* ps);//返回栈顶元素
EXTERN_C int StackSize(ST* ps);//栈里的元素个数

还有一种写法(extern "C"可以加在函数声明的前面,也可以括起来多个函数声明)

#ifdef __cplusplus
extern "C"
{
#endif
	void StackInit(ST* ps);//初始化
	void StackDestory(ST* ps);//销毁
	void StackPush(ST* ps, STDataType x);//入栈
	void StackPop(ST* ps);//出栈
	bool StackEmpty(ST* ps);//判断栈是否为空
	STDataType StackTop(ST* ps);//返回栈顶元素
	int StackSize(ST* ps);//栈里的元素个数
#ifdef __cplusplus
}
#endif

总结:

C和C++对函数的命名规则不同,这是C++支持函数重载的原因,也是C和C++相互调用编译失败的原因,为了让它们能够相互调用,我们提出了extern "C"的概念

本文涉及到了编译和预处理等底层逻辑,建议大家先弄懂👉c语言自学教程——程序环境和预处理。磨刀不误砍柴工,祝大家一步步脚踏实地,不断进步。

目前在不断更新<C++语言>的知识总结,已经更新完了<C语言><数据结构初阶>,未来我会系统地更新<Linux系统编程><Linux网络编程><数据结构进阶><MySQL数据库>等内容。想要系统学习编程的小伙伴可以关注我!

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 17:54:42  更:2022-06-25 17:56:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 12:42:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码