目录:
例1:函数重载(nm指令查看) | C/C++混合编程 |
---|
1. 默认参数的目的 | 2. 默认参数的声明 | 3. 默认参数的顺序规定 |
---|---|---|
4. 默认参数与函数重载(易错) | 默认参数的注意点 |
函数重载
在实际开发中,有时候需要实现几个功能类似的函数,只是细节有所不同。 如交换两个变量的值,但这两种变量可以有多种类型,short, int, float等。在C语言中,必须要设计出不同名的函数,其原型类似于:
void swap1(short *, short *);
void swap2(int *, int *);
void swap3(float *, float *);
但在C++中,这完全没有必要。
C++ 允许多个函数拥有相同的名字,只要它们的 参数列表不同 就可以,这就是函数重载(Function Overloading)。
借助重载,一个函数名可以有多种用途。函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。
C++进行函数重载的实现原理叫名字改编(name mangling),具体的规则是:
- 函数名称必须相同 。
- 参数列表必须不同(参数的类型不同、个数不同、顺序不同)。
- 函数的返回类型可以相同也可以不相同。
- 仅仅返回类型不同不足以成为函数的重载。
对函数进行名字改编,当函数的参数顺序、个数、类型不一样的时候,进行修改,返回值要相同
例1:函数重载
#include <iostream>
using std::cout;
using std::endl;
int add(int val1, int val2)
{
return val1 + val2;
}
int add(int val1, int val2, int val3)
{
return val1 + val2 + val3;
}
int add(double val1, int val2)
{
return val1 + val2;
}
int main()
{
int val1 = 1, val2 = 2;
double dVal = 2.55;
cout << "val1 + val2 = " << add(val1, val2) << endl;
cout << "val1 + val2 + val1 = " << add(val1, val2, val1) << endl;
cout << "dVal + val1 = " << add(dVal, val1) << endl;
return 0;
}
使用 g++ -c 编译产生 .o 文件,然后通过nm
指令(列举文件中的符号) .o文件查看编译器如何区分重载函数。
U __cxa_atexit
U __dso_handle
U _GLOBAL_OFFSET_TABLE_
000000000000019c t _GLOBAL__sub_I__Z3addii
000000000000004c T main
0000000000000030 T _Z3adddi //add的第3个重载函数int add(double, int)
0000000000000000 T _Z3addii //add的第1个重载函数int add(int, int)
0000000000000014 T _Z3addiii //add的第2个重载函数int add(int, int, int)
0000000000000153 t _Z41__static_initialization_and_destruction_0ii
U _ZNSolsEi
U _ZNSolsEPFRSoS_E
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
U _ZSt4cout
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
0000000000000000 r _ZStL19piecewise_construct
0000000000000000 b _ZStL8__ioinit
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
C++支持函数重载,因此编译器编译函数的过程中参数类型也加到编译后的代码中,看到add函数编译器编译实际的重载函数的区分
tip: C++如果想兼容C,就必须要按照C的方式去编译C的代码,接下来看一下C与C++的混合编程的方式,
extern "C"后,会指示编译器这部分代码按C语言(而不是C++)的方式进行编译。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。
为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。
主要用在下面的情况:
- C++代码调用C语言代码
- 在C++的头文件中使用
- 在多个人协同开发时,可能有的人比较擅长C语言,而有的人擅长C++,这样的情况下也会有用到
extern “C” 可以在C++中按照C的方式编译。
内置宏 #ifdef __cplusplus 和 #endif 使C的源码既能在C中以C的形式进行调用,也能在C++中以C的形式进行调用
#ifdef __cplusplus
extern "C"
{
#endif
//函数
int add(int val1, int val2)
{
return val1 + val2;
}
#ifdef __cplusplus
}//end of extern "C"
#endif
默认参数
1. 默认参数的目的
C++可以给函数定义默认参数值。通常,调用函数时,要为函数的每个参数给定对应的实参。
void func1(int x, int y);
void func1(int x, int y)
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
}
无论何时调用func1函数,都必须要给其传递两个参数。但C++可以给参数定义默认 值,如果将func1函数参数中的x定义成默认值0, y定义成默认值0,只需简单的将 函数声明改成
void func1(int x = 0, int y = 0);
这样调用时,若不给参数传递实参,则func1函数会按指定的默认值进行工作。允许函数设置默认参数值,是为了让编程简单,让编译器做更多的检查错误工作。
2. 默认参数的声明
一般默认参数在函数声明中提供。当一个函数既有声明又有定义时,只需要在其中一个中设置默认值即可。若在定义时而不是在声明时置默认值,那么函数定义一定要在函数的调用之前。因为声明时已经给编译器一个该函数的向导,所以只在定时设默认值时,编译器只有检查到定义时才知道函数使用了默认值。若先调用后定义,在调用时编译器并不知道哪个参数设了默认值。所以我们 通常是将默认值的设置放在声明中而不是定义中。
3. 默认参数的顺序规定
如果一个函数中有多个默认参数,则形参分布中,默认参数应从右至左逐渐定义。 当调用函数时,只能向左匹配参数。如:
void func2(int a = 1, int b, int c = 0, int d); //error
void func2(int a, int b, int c = 0, int d = 0); //ok
若给某一参数设置了默认值,那么在参数表中其后所有的参数都必须也设置默认值,否则,由于函数调用时可不列出已设置默认值的参数,编译器无法判断在调用时是否有参数遗漏。
4. 默认参数与函数重载
默认参数可将一系列简单的重载函数合成为一个。例如:
void func3();
void func3(int x);
void func3(int x, int y);
//上面三个函数可以合成下面这一个
void func3(int x = 0, int y = 0);
如果一组重载函数(可能带有默认参数)都允许相同实參个数的调用,将会引起调用的二义性。( 相同实參个数的调用,编译器不知道该调用哪一个)
int func(int);
int func(int val1, int val2);
int func(int val1, int val2 = 1);
int func(int val1 = 1, int val2 = 1);
int func(int val1 = 1, int val2 = 1, int val3 = 1);
//参数数量 >= 0的函数都可以调用,编译器不知道该调用哪一个
cout << "func() = " << func() << endl;
//参数数量 >= 1 的函数都可以调用,编译器不知道该调用哪一个
cout << "func(10) = " << func(val1) << endl;
//参数数量 >= 2 的函数都可以调用,编译器不知道该调用哪一个
cout << "func(10, 20) = " << func(val1, val2) << endl;
//参数数量 >= 3 的函数都可以调用,只有1个函数,所以只有这一句时不会报错
cout << "func(10, 20, 30) = " << func(val1, val2, val3) << endl;
所以在函数重载时,要谨慎使用默认参数。
默认参数的注意点:
-
有函数声明(原型)时,默认参数可以放在函数声明或者定义中,但只能放在二者之一。(最好在声明)
-
没有函数(原型)时,默认参数在函数定义时指定。(定义时,函数调用应在函数定义之后)
-
在具有多个参数的函数中指定默认值时,默认参数都必须出现在不默认参数的右边,一旦某个参数开始指定默认值,它右边的所有参数都必须指定默认值。
-
在调用具有默认参数的函数时,若某个实参默认,其右边的所有实参都应该默认。
-
函数重载时,谨慎使用默认参数,防止二义性。