函数默认参数
C++中允许为函数提供默认参数,又名缺省参数。
语法: 返回值类型 函数名 (参数类型 参数名 = 默认值){ // .... }
- 在笔者的C++14(__cplusplus-201402、gcc-8.1.0)环境中,若有函数声明时,默认参数必须要放在函数声明中(而在一些版本中,编译器规定默认参数要写在二者其中的任意一个,两者不可兼得)。
c++版本可以通过下面代码查看:int SeeVersion() { int version=__cplusplus; cout << version << endl; }
C++版本对应表
clang 和 gcc 判断 C++版本 __cplusplus
的值C++ 17 201703L C++ 14 201402L C++ 11 201103L C++ 03 以下 199711L msvc 判断 _MSVC_LANG
_MSVC_LANG
的值-- -- C++ 17 201703L C++ 14 201402L C++ 11 201103L C++ 03 以下 199711L 该表引用文章:C++ 判断标准版本和编译器 - c++用法
float func(float f = 2.0); //函数声明 float func(float f) //函数定义 {// .... }
- 没有函数声明时,默认参数在函数定义时指定.
float func(float f = 1.0) //函数定义
{ // .... }
- 在具有多个参数的函数中指定默认值时,默认参数都必须出现在不默认参数的右边,一旦某个参数开始指定默认值,它右边的所有参数都必须指定默认值.
int func (int a, int b= 2, int c= 3){ // .... } // 正确
int func (int a, int b= 2, int c){ // .... } // 错误, c未指定默认值
int func (int a= 1, int b, int c= 3){ // .... } // 错误, b未指定默认值
- 在调用具有默认参数的函数时, 若某个实参默认,其右边的所有实参都应该默认。
int func (int a= 1, int b=2, int c= 3); //调用函数 func()
func(); //正确, a=1, b=2, c=3
func(3); //正确, a=3, b=2, c=3
func(2, 3); //正确, a=2, b=3, c=3
func(4, 5, 6); //正确, a=4, b=5, c=6
func(, 2, 3); //错误, i1默认,其右边的i2和i3没有默认
函数占位参数
c++中函数的形参列表里可以有占位参数,用来占位,调用函数时必须填补位置
语法: 返回值类型 函数名 (数据类型) { // ....}
在"Thingking in C++"中是这么解释:
这种语法允许你给函数设置一个参数而不用使用它,作用是当你想要去修改函数定义的时候,可以不用再去一个个修改所有调用该函数的地方(就比如原来中间这个参数在方法中是被使用的,但后来不用了)。当然你会说为什么不可以直接在方法中删除所有使用该参数的地方呢?原因是这样的话编译器就会报警告,说函数定义中定义了该参数但是却没有使用它。
看完这段话,笔者对占位参数理解是: 在我们认为方法中某个参数无用时想要删除时,但暂时又不想大量修改函数调用的实参列表(避免修改造成更大的影响面)。C++为我们提供了这种占位参数来避免编译器的报错。但笔者觉得这种占位参数操作导致了实参列表中参数数量与实际用到的参数数量不是对应的,产生了脏数据,在一定程度上造成了项目的混乱。笔者不喜欢这样,我提倡尽量避免不洁数据的存在,但在项目体量非常大的时候,存在大量的引用,有时我们会认为“此时修改会造成更大的影响面,修改可能会发生错误”的时候,那么这时候综合考虑后才能用这种占位操作作为临时的解决方案,推迟到后期专门解决。
函数重载
在C++中,同一个作用域内,可以声明几个功能类似的同名函数,但是这些函数的签名(指函数名字、参数的个数、种类、类型或者顺序)必须不同。不能仅通过返回类型的不同来重载函数。由这些不同签名的函数构成重载。
下面的实例中,同名函数func2
用于测试:
int main()
{
float i=1;
func2(1,2);
func2(1.0f,2);
func2(i,2);
system("pause");
return 0;
}
int func2(int a,int b)
{
cout << "int" << endl;
return a+b;
}
int func2(float a,int b)
{
cout << "float" << endl;
return a+b;
}
当上面的代码被编译和执行时,它会产生下列结果:
int
float
float
注意事项:
- const修饰引用变量与纯引用变量属于不同签名可以构成重载,但const修饰变量与普通变量属于同一签名不能构成重载。
const修饰变量与普通变量不能构成重载:
而const修饰引用变量与纯引用变量却属于不同签名可以构成重载int main() { float i=1; const float k=1; func3(i,2); func3(k,2); system("pause"); return 0; } int func3(float &a,int b) { cout << "float reference" << endl; return a+b; } int func3(const float &a,int b) { cout << "const float reference" << endl; return a+b; }
打印输出:
float reference const float reference
为什么呢?在第一个例子const修饰int变量中fun(int i)和fun(const int i)是一样的,两个函数都不会改变a的值,因为函数调用中形参只是拷贝了实参的值,是值传递的。这两个函数编译器是没有任何区别的,没有任何意义的。所以编译器判定它就是同一种签名不允许这两个函数定义通过编译。
在第二个例子中const修饰引用变量与纯引用变量两个函数func3(float &a,int b)
,func3(const float &a,int b)
中实参-形参的传递方式都是地址传递,即将实参地址传递给形参,形参引用着实参的地址。func3(float &a,int b)
会改变实参的值,而加上const修饰的引用形参因为是常量引用所以不允许修改值。这样的两个函数执行的效果是不同的。所以编译器认定它们的签名不相同,可以构成函数重载。 - 有默认函数的函数重载,可能会存在二义性
示例:int func4(int a) { return a; } int func4(int a,int b=2) { return a+b; }
上面代码虽然允许我们编译,但是当我们调用函数时
func4(4)
,编译器会发生报错。
这是因为两个重载都能与实参列表匹配,编译器不知道到底该调用哪个函数就引起二义性的错误。
本文案例代码下载: