1.7 函数
1.7.1 函数缺省参数
在C++中,函数的形参列表中的形参是可以有默认值的。有默认值的参数即为默认参数。
在函数调用时,有默认参数可以缺省。
语法:返回值类型 函数名 (参数= 默认值){函数体}
#include <iostream>
using namespace std;
int add(int x, int y, int z = 100) {
return x + y + z;
}
int main(void) {
cout << add(3, 3) << endl; //106
cout << add(3, 3, 4) << endl; //10
return 0;
}
注意事项:
- 靠右原则:如果某个位置参数有默认值,那么从这个位置往后,从左往右,必须都要有默认值。
#include <iostream>
using namespace std;
int add(int x, int y = 100, int z) { //error
return x + y + z;
}
int main(void) {
cout << add(3, 3) << endl;
cout << add(3, 3, 4) << endl;
return 0;
}
- 函数声明和函数实现(即函数定义),只允许其中一个有默认值,即如果函数声明有默认值,则函数实现的时候就不能有缺省参数。
#include <iostream>
using namespace std;
int add(int x, int y, int z = 100);//声明这有默认值了
int main(void) {
cout << add(3, 3) << endl;
cout << add(3, 3, 4) << endl;
return 0;
}
int add(int x, int y, int z) { //定义这就不能再有了
return x + y + z;
}
1.7.2 哑元
只有类型而没有变量名的参数成为“哑元”。
int func(int a, int ) {
……
}
需要使用哑元的场景:
- 兼容旧代码,保证函数的向下兼容性
void func(int i, int j){...}==升级==>void func(int i){...}
void func(int i, int j){...}==升级==>void func(int i, int /*哑元*/){...}
- 操作符重载中,区分前后++/- -
1.7.3 引用参数
-
可以将引用用于函数的参数,这时形参就是实参的别名
-
引用型函数参数作用
-
在函数中修改实参的值
#include <iostream> using namespace std; void swap(int &, int &) ; int main (void) { int x = 100; int y = 200; swap(&x, &y); cout << "x : " << x << "\t" << "y : " << y << endl; getchar(); return 0; } void swap(int &a, int &b) { int temp = a; a = b; b = temp; }
-
避免实参到形参数值复制的开销,提高传参效率
#include <iostream> using namespace std; struct Teacher{ char name[100]; int age; }; void print(Teacher &st){ cout << st.name << ',' << st.age << endl; } int main(void) { Teacher st = {"王钢蛋", 18}; print(st); return 0; }
-
-
引用型函数参数可能意外修改实参,如果不希望通过引用修改实参本身,可以将其声明为常引用(const),在提高传参效率的同时还可以接收常量型实参
#include <iostream> using namespace std; void add(const int &x, const int &y){ // x++; //error cout << "x + y = " << x+y << endl; } int main (void) { int x = 3, y = 5; add(x,y); return 0; }
1.7.4 返回引用
-
可以将函数的返回类型声明为引用型,这时函数的返回结果就是return后面数据的别名,避免了函数返回值的开销
-
函数中返回引用,一定要保证在函数返回以后,该引用的目标依然有效
- 可以返回全局变量、静态变量和成员变量的引用
- 可以返回引用型参数的本身
- 可以返回调用对象自身的引用
- 可以返回堆中动态创建的引用
- 不能返回局部变量的引用 / 非常危险
#include <iostream> using namespace std; int &add(int &a, int &b) { int temp = 0; temp = a+b; return temp; //风险 局部函数结束时内存空间回收 } int main(void) { int x = 3, y = 5; int &t = add(x,y); t++; //通过别名操作 已经被系统回收的内存 cout << t << endl; return 0; }
1.7.5 函数重载
-
基本作用
同一作用域,函数名相同,但是当中的参数必须有所区分(类型、个数、顺序),将构成重载关系
#include <iostream> using namespace std; void swap(int *a, int *b) { cout << "a + b = " << *a + *b << endl; } void swap(char *a, char *b) { cout << "a + b = " << *a + *b << endl; } void swap(float *a, float *b) { cout << "a + b = " << *a + *b << endl; } int main(void) { int a = 3, b = 5; swap(&a, &b); char c1 = 'a', c2 = 'b'; swap(&c1, &c2); float f1 = 3.1, f2 = 3.3; swap(&f1, &f2); return 0; }
-
重载的实现
编译器将形参变量的类型作为最终函数名的一部分。
nm a.out
注意
1)形参变量名不同 不构成重载的要素
void swap(int *a, int *b); void swap(int *sa, int *sw); //形参变量名不同
2)函数返回类型不同 不构成重载的要素
int swap(int *a, int *b); void swap(float *a, float *b);//函数返回类型不同
-
函数匹配的优先级
当前g++编译器匹配的一般规则:
1)完全匹配 //最高
2)常量转换 //良好
3)升级转换 //一般
4)降级转换 //较差
5)省略号匹配 //最差
#include <iostream> using namespace std; void swap(int x) { cout << "swap(int)" << endl; } void swap(int x, int y) { cout << "swap(int, int)" << endl; } void bar(int c) { cout << "bar(int)" << endl; } void bar(const char c) { cout << "bar(const char)" << endl; } void foo(int f){ cout << "foo(int)" << endl; } void foo(char c) { cout << "foo(char)" << endl; } void hum(int i, ...) { cout << "hum(int...) " << endl; } void hum(int i, int j) { cout << "hum(int, int)" << endl; } int main(void) { int x = 10; swap(x); char c = 'a'; bar(c); // 常量转换 char -------> const char short s = 10; foo(s); // 升级转换 short --------> int hum(2, 3.14); // 降级转换 double --------> int return 0; }
注意二义性问题:
-
默认类型转换带来的二义性
void func(unsigned int x) { cout << "func(unsigned int)" << endl; } void func(double x) { cout << "func(double)" << endl; } int j = 10; //func(j); //g++ 编译器不知道调用那个函数了 func((unsigned int) j);//需要程序员自己确认需要调用那个
-
缺省参数带来的二义性
void tem(int x, int y, int z = 200){ cout << "tem(int, int, int)" << endl; } void tem(int x, int y){ cout << "tem(int, int)" << endl; } tem(20, 30) //error