4.2 缺省参数分类
4.2.1 全缺省参数
全缺省参数就是函数的每个形参都有一个默认值,所以在调用函数的时候不用传实参,要传实参的话就必须从左到右依次去传。但是在传参的时候不能跳着传,例如下面:
void func(int a = 10, int b = 20, int c = 30) { cout << a << " " << b << " " << c << endl; } int main() { func(1); //可以传一个 func(1, 2); //可以传两个 func(1, 2, 3); //可以全部传 //我们传参是从左到右依次传 return 0; }
运行结果:
跳着传参:
C++缺省函数的调用压根就没有跳过传的概念,这样是不可以的。
4.2.2 半缺省参数
半缺省是多个形参一部分给缺省值,但是给缺省值的时候必须从右往左缺省,也是不能跳着缺省。
我们写一个代码来看看半缺省:
//半缺省 void func(int a, int b = 20, int c = 30) { cout << a << " " << b << " " << c << endl; } int main() { func(1); //第一个必须传 func(1, 2); //第二个可以传,可以不传 func(1, 2, 3); //后两个都是可传,可不传 //我们传参是从左到右依次传 return 0; }
运行结果:
我们可以看到上面的代码中,我们写的func函数的第一个形参a是没有给默认值的,因此在调用的时候第一个参数必须传,不传就会出错。
学会了缺省参数,那它的用途是什么呢?
在栈中,我们使用的时候就已经知道我们要预先开辟1000个空间的时候,就可以使用缺省参数来写。
struct Stack { int* a; int size; int capacity; }; void StackInit(Stack* ps, int n = 4) { assert(ps); ps->a = (int*)malloc(sizeof(int) * n); if (NULL == ps->a) { perror("malloc fail:"); return; } ps->size = 0; ps->capacity = n; } int main() { Stack st; StackInit(&st, 1000); return 0; }
这里我们就用到了半缺省参数,如果我们只是初始化,但是不知道多大空间合适,可以选择不传,初始化的时候只会开4个空间,也不会造成浪费。这就是缺省参数的灵活运用了。
5、函数重载
5.1 函数重载概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
a. 我们可以根据形参类型的不同但是函数名相同来调用函数。
int Add(int x, int y) { return x + y; } double Add(double x, double y) { return x + y; } int main() { cout << Add(1, 2) << endl; cout << Add(1.1, 2.2) << endl; return 0; }
运行结果:
上面的代码我们可以看到,虽然它们的名字都是Add,但是参数的类型不同,我们在调用的时候传入的实参会根据类型去调用名字相同但是类型不同的函数,因此类型不同构成重载。
b. 还可以根据形参类型顺序的不同但是函数名相同来调用函数。
void func(int x, double y) { cout << "void func(int x, double y)" << endl; } void func(double x, int y) { cout << "void func(double x, int y)" << endl; } int main() { func(1, 1.1); func(1.1, 1); return 0; }
运行结果:
这里的两个func函数的形参都是一个整型,一个浮点型,但是两个形参的顺序不同,在函数调用的时候,传的实参会根据类型的不用在调用的时候,调用不同的函数,因此参数顺序不同构成重载。
c. 参数个数不同,但函数名相同来调用函数。
void func(int x, double y) { cout << "void func(int x, double y)" << endl; } void func(int x, double y, int z) { cout << "void func(int x, double y, int z)" << endl; } int main() { func(1, 1.1); func(1.1, 1, 2); return 0; }
运行结果:
这里的参数个数不同,第一个函数两个参数,第二个函数三个参数,调用的时候传的参数个数不同,所调用的函数也就不同,因此参数个数不同构成函数重载。
写到这了,肯定会有人想,那返回值类型不同是不是也可以构成重载呢?
我们先回答一下:不可以。我们来验证一下:
short func(short x, short y) { return x + y; } int func(short x, short y) { return x + y; }
这里可以看到,返回类型不同无法构成重载。
总结:函数在名字相同的前提下,参数的类型不同,参数的顺序不同,参数的个数不同,这三个情况下都可以构成函数重载。
5.2 C++支持函数重载的原理--名字修饰(name Mangling)
为什么C++支持函数重载,而C语言不支持函数重载呢?
在C/C++中,一个程序运行起来需要经过四个阶段:预处理,编译,汇编,链接。
这四个阶段的流程分别是(以Linux的流程来说):
1、预处理:头文件展开,宏替换,去掉注释,条件编译,生成test.i
2、编译:检查语法,生成汇编(指令级代码),生成test.s
3、汇编:将汇编代码转为二进制机器码,生成test.o
4、链接:合并链接,生成可执行程序,a.out
C++函数重载主要就是在链接的时候用函数的名字找地址,C语言中用也是如此。但是C++引入了函数名修饰规则,这就是C++支持函数重载的根本原因。我们以Linux环境来展开说。
结合上面的两个截图我们能总结出来,g++下的函数名修饰规则是:
_Z 函数名长度 函数名 形参的首字母
因此函数名就算相同,但是类型不同,顺序不同,个数不同都会产生不同的函数名。
函数调用的时候指令就是call 函数名,而修饰过的函数名跳转后会找到对应的函数的地址,这样就能找到对应的函数。这就是为什么函数名修饰规则修饰后,根据不同的函数名在符号便中找到对应的函数名,也就找到了对应的地址,最终跳转过去,调用对应函数的原因。
C语言不行的原因是没有像C++这样的函数名修饰规则,它的函数名直接存放在符号表中,如果是相同的函数名,一进入符号表就乱了,它不会再加上类型的首字母这样区分函数,因此C语言实现不了函数重载。