C++支持函数重载的原因
C语言编译结果(简化)
函数名不同,在符号表中才能区分出来,所以不能支持重载
C++编译结果(简化)
C++是通过函数修饰规则在符号表中区分的,只要参数不同,修饰的出的函数名就不同,所以支持重载
重载的概念一直都没有提到返回值,也就是说返回值并不会构成重载原因,但是为什么呢???
int test(int a, int b) { cout << "test(int a,int b)" << endl; return a + b; } char test(int a, int b) { cout << "test(int a,int b)" << endl; return a + b; } int main() { test(0,0); test(1, 1); return 0; }
在函数调用时存在二义性,没有指定返回值类型,无法进行区分,并不是函数名修饰规则的原因,所以不构成重载
引用
引用概念
引用是给已经存在的变量另取一个名字或者说小名,但是不会为引用变量新开辟内存空间,所以变量和引用变量共用同一块内存空间
变量类型&引用变量名称=引用实体
#include<iostream> using namespace std; int main() { int i = 1; int& ri = i; printf("%p\n", &i); printf("%p\n", &ri); return 0; }
既然是对已存在的变量进行引用,当然数据类型要求是一致的
引用特性
引用在定义时必须要初始化
一个变量可以有多个引用(小名)
若引用(小名)已经引用一个变量,则不能再引用其他变量;即一个小名不能同时称呼两个人
具体应用
形式参数
#include<iostream> using namespace std; void Swap(int& ra, int& rb) { int tmp = ra; ra = rb; rb = tmp; } int main() { int a = 0; int b = 1; printf("%d %d\n", a, b); printf("%p %p\n", &a, &b); Swap(a, b); printf("%d %d\n", a, b); printf("%p %p\n", &a, &b); return 0; }
优点
减少拷贝,提高效率
输出型参数,在函数中修改形参,实参也随之改变
返回值
引用返回
int& Test() { static int i = 0; i++; return i; } int main() { int ret = Test(); cout << ret << endl; return 0; }
函数栈帧的使用是从上往下(高地址到低地址),当 函数int& Test()结束时,栈帧被销毁。变量i保存在静态区,所以并不会被销毁,然后返回i的别名给 ret
如果变量i不在静态区
int& Test() { int i = 0; cout << &i << endl; i++; return i; } void test() { int a = 0; cout << &a << endl; } int main() { int& ret = Test(); cout << ret << endl; cout << ret << endl; test(); cout << ret << endl; cout << &ret << endl; return 0; }
int& ret = Test()->int&ret = int& i
所以 ret 也就是 i 本身
由于变量i是 int& Test()内的局部变量,所以当函数栈帧销毁时,i的空间也被收回。可以继续访问,但是数据是不确定的,而且 使用cout也会调用函数的
当函数 test()开辟栈帧时,碰巧使用前面函数 Test()函数栈帧的空间,也就是说,当访问 ret 时,其实就是访问变量 a 本身
运行结果也表面,两个函数 Test(), test(),开辟栈帧使用的确实是同一块空间
第一次打印 ret 时,数据没有改变,可能是 ret 是作为参数传递给 cout,所以数值没有改变
第二次打印 ret 时,数据是随机值,也就是 cout调用函数将被收回 i 的空间数据进行了改变
传值返回
int Test() { static int i = 0; i++; return i; } int main() { int ret = Test(); cout << ret << endl; return 0; }
与引用返回类似,变量 i 存放在静态区,栈帧销毁不会收到影响,反而作为临时变量返回给 ret
如果变量i不在静态区
int Test() { int i = 0; i++; return i; } int main() { int ret = Test(); cout << ret << endl; return 0; }
由于变量i是 int& Test()内的局部变量,所以当函数栈帧销毁时,i的空间也被收回。可以继续访问,但是数据是不确定的,而且 使用cout也会调用函数
总结
出了函数作用域,返回变量不存在,不能使用引用返回,引用返回的结果是未定义的(未知的)
出了函数作用域,返回变量还存在,才能使用引用返回
int Test1() { int i = 0; i++; return i; } int Test2() { static int i = 0; i++; return 0; }
优点
减少拷贝,提高效率
可修改返回值
变量没有被const修饰时一般是可读可写的,可读可写是权限,在程序中,权限可以缩小,平移,但是不可以扩大
int main() { //a可读可写 int a = 0; //ra是a的别名,同样是可读可写,权限平移 int& ra = a; //rra被const修饰,只能读,权限缩小 const int& rra = a; //b被const修饰,只能读 const int b = 0; //rb是b的别名,可读可写,权限扩大 error int& rb = b; //rb被const修饰,只能读,权限平移 const int& rb = b; return 0; }
结合上面的传引用做参数,一般都是需要const修饰,否则程序会报错
void test(int& x) {} int main() { int a = 0; const int& rra = a; test(a); test(rra); return 0; }