3 缺省参数
大家知道什么是备胎吗?
缺省参数差不多就是充当备胎这么一个角色
而缺省型参数又分为全缺省参数和半缺省参数。
3.1 全缺省参数
#include<iostream> using namespace std; int Add(int x = 10, int y = 20, int z = 30) { return x + y + z; } int main() { cout << Add(1, 2, 3) << endl; cout << Add(1, 2) << endl; cout << Add(1) << endl; cout << Add( ) << endl; return 0; }
输出结果:
我们发现参数的接受是从左到右的。
3.2 半缺省参数
void Func(int a, int b = 10, int c = 20) { cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "c = " << c << endl << endl; } int main() { Func(1); Func(1, 2); Func(1, 2, 3); return 0; }
注意:
1. 半缺省参数必须从右往左依次来给出,不能间隔着给
这种方式是错误的:
2. 缺省参数不能在函数声明和定义中同时出现
我们重新定义一个Func.h的头文件,将Func的声明放进去,然后编译:
我们只需要将缺省参数放在其中一个就行了,一般是定义在声明中。
3. 缺省值必须是常量或者全局变量
4. C语言不支持(编译器不支持)
4. 函数重载
自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前 者是 “ 谁也赢不了! ” ,后者是 “ 谁也赢不了! ”
4.1 函数重载概念
函数重载 : 是函数的一种特殊情况, C++ 允许在 同一作用域中 声明几个功能类似 的同名函数 ,这些同名函数的形参列表 ( 参数 个数 或 类型 或 顺序 ) 必须不同 ,常用来处理实现功能类似数据类型不同的问题.
int Add(int left, int right) { return left + right; } int Add(int left, char right) { return left + right; } int Add(char right, int left) { return left + right; } int Add(char right, int left, int mid) { return left + right + mid; } int main() { cout << Add(1, 2) << endl; cout << Add(1, 'a') << endl; cout << Add('a', 1) << endl; cout << Add(1, 'a', 2) << endl; return 0; }
上面这些都构成函数重载:
但是这种呢?
int Add(int left, int right) { return left + right; } char Add(int left, int right) { return left + right; } int main() { Add(1, 3); return 0; }
很明显,此时编译器就报了错误。注意:返回值不同不能构成重载。因为函数调用时你不知道到底要调用哪一个。
这种能构成重载吗?
void f() { cout << "f( )" << endl; } void f(int x = 0) { cout << "f( x=0 )" << endl; }
我们编译一下代码发现能够跑过,说明这是构成重载的,但是如果你调用该函数带上参数还好,如果不带参数那编译器就不知道要调用哪个函数了,这是就会报错。
4.2 名字修饰(name Mangling)
为什么C++支持函数重载,而C语言不支持函数重载呢?
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
有关具体的详解可以参考我的这篇博客程序环境和预处理
1. 实际我们的项目通常是由多个头文件和多个源文件构成,而通过我们 C 语言阶段学习的编译链接,我们可以知道,【当前 a.cpp 中调用了 b.cpp 中定义的 Add 函数时】,编译后链接前, a.o 的目标文件中没有 Add 的函数地址,因为 Add 是在 b.cpp 中定义的,所以 Add 的地址在 b.o 中。那么怎么办呢?
2. 所以链接阶段就是专门处理这种问题, 链接器看到 a.o 调用 Add ,但是没有 Add 的地址,就会到 b.o 的符号表中找 Add 的地址,然后链接到一起 。
3. 那么链接时,面对 Add 函数,链接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。
4. 由于 Windows 下 vs 的修饰规则过于复杂,而 Linux 下 gcc 的修饰规则简单易懂,下面我们使用了 gcc 演示了这个修饰后的名字。
5. 通过下面我们可以看出 gcc 的函数修饰后名字不变。而 g++ 的函数修饰后变成【 _Z+ 函数长度 + 函数名 + 类型首字母】。
采用 C 语言编译器编译后结果 :
结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。
采用C++编译器编译后结果 :
结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息
添加到修改后的名字中。
Windows下名字修饰规则 :
对比Linux会发现,windows下C++编译器对函数名字修饰非常诡异,但道理都是一样的。
【扩展学习:C/C++函数调用约定和名字修饰规则】
这里大家有兴趣了解下就好。
6. 通过这里就理解了 C 语言没办法支持重载,因为同名函数没办法区分。而 C++ 是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
7. 另外我们也理解了,为什么函数重载要求参数不同!而跟返回值没关系。
面试题:
1. 下面两个函数能形成函数重载吗?有问题吗或者什么情况下会出问题?
void TestFunc(int a = 10) { cout<<"void TestFunc(int)"<<endl; } void TestFunc(int a) { cout<<"void TestFunc(int)"<<endl; }
回答:很明显这两个函数是无法构成函数重载的,因为参数个数 或 类型 或 顺序是相同的,传了参数后也无法识别调用哪一个函数。
2. C 语言中为什么不能支持函数重载?
回答:因为编译的时候两个重载函数的函数名相同,符号表中会存在歧义和冲突,其次在链接时也会有冲突,因为他们都是直接用函数名去标识和查找。
3. C++中函数重载底层是怎么处理的?
回答:因为C++不是直接用函数名去标识和查找函数的,而是用函数名修饰规则,只要参数的个数,类型,顺序不同,符号表的函数就不会存在歧义和冲突了,链接时去调用两个重载函数查找地址时也是明确的。
5 总结
本文介绍了命名空间的定义和使用,C++输入输出的使用,缺省参数以及函数重载,重点讲解了函数重载是怎样实现的以及C语言不支持函数重载C++支持函数重载的原因。引用将放在下一篇博客来讲。
如果该文对你有用,能不能一键3连支持一下播主呢