目录
一.引用 返回和值返回的区别
1.值返回
当n是全局变量:
int Count1() {
static int n = 0;
n++;
//....
return n;
}
int main() {
int ret = Count1();
cout << ret << endl;
return 0;
}
调用Count1时会为其建立栈帧, 因为变量n是全局变量,n不在栈区而在静态区,之后Count1函数结束,栈帧被销毁,但n的生命周期是全局的,执行return n时n还在,这样ret就接收到函数的返回值了。
而当n是局部变量时:
int Count2() {
int n = 0; //局部变量
n++;
//....
return n;
}
int main() {
int ret2 = Count2();
cout << ret2 << endl;
return 0;
}
因为n是局部变量,当函数即将结束时,n会被销毁,此时,执行return n会让n在栈帧外开辟一个临时变量,里面存放n的值(拷贝),而ret指向Count2的返回值,又会再进行对n的临时变量进行接收(再拷贝一次)。
所以值传递效率低:会进行两次拷贝,时间效率较低。
2.引用返回:
当n是全局变量:
//引用返回
int& Count3() {
static int n = 0;
n++;
//....
return n;
}
int main() {
int ret = Count3();
cout << ret << endl;
return 0;
}
用ret接收Count3的引用返回时,n会为其开辟临时变量,但这个变量并不开辟空间,所以整个过程只进行了一次拷贝,效率高。
当n是局部变量:
int& Count4() {
int n = 0;
n++;
//....
return n;
}
int main() {
int ret = Count4();
cout << ret << endl;
return 0;
}
n为局部变量,函数栈帧被销毁时,n也没了,所以返回n时,使用临时变量(n的别名)会越界,ret属于非法访问n变量(相当于野指针的访问),虽然不会报错,但是属于非法操作。
n被销毁,这块空间还给了操作系统,空间还在,也可以再进行访问,只不过是非法访问而已,但之前存储的数据是不被保护的,空间可能会分配给其他人,那么这块空间原有的数据也会被覆盖。 就好比住酒店房间时,我们放了很多东西在房间,游玩了几天后,退房时将房卡还给了酒店工作人员,离开酒店后才想起当时自己的毛巾落在了酒店,但那个房间已经不属于我了,这时有两种情况:
一.我偷偷回到房间,毛巾还在,这时房间还未分配给其他人;
二.我回来的时候,房间已经被保洁阿姨打扫过了(门还开着),我悄悄进去发现毛巾已经不在那里了,那里放着一卷纸(被覆盖了数据)。
根据解释,模拟实现上面这个说的酒店案例:
int& Count4() {
int n = 0;
n++;
//....
return n;
}
void Func() {
int x = 100;
}
int main() {
int& ret = Count4();
cout << ret << endl;
Func();
cout << ret << endl;
return 0;
}
运行结果:
为什么在调用Func函数后,ret就变成了100了?
此时的Func函数中所开辟的栈帧位置就是之前Count4函数的栈帧位置, Count4函数被销毁,里面的整型变量n也还给了操作系统,而后Func开辟栈帧,里面同样也是开辟了一个整型变量,与n同样大小,原来变量n的所处空间被OS复用给了Func函数,其空间原有的数值被变量x所覆盖,那么意味着这块空间属于变量x了,又因为之前ret被定义为是这块空间的别名,所以ret的值也就变成了100。
int& Count4() {
int n = 0;
n++;
//....
cout <<"变量n的地址:"<< & n << endl;
return n;
}
void Func() {
int x = 100;
cout << "变量x的地址:" << &x << endl;
}
int main() {
int& ret = Count4();
cout << ret << endl;
cout <<"ret的地址:"<< & ret << endl; //取ret的地址
cout << endl; //换行
Func();
cout << ret << endl;
cout << "调用Func后ret的地址:" << &ret << endl; //取ret的地址
return 0;
}
结果:
我们发现: 变量n的地址与变量x的地址完全一样,而调用各函数后,ret的地址也是相同的,更加印证了之前的推论,所以引用返回不能这样使用,ret的地址不可与函数中变量的地址相同!!!
总结:
1.出了作用域,且返回的变量不存在,不可用引用返回,因为引用返回导致的结果是不确定的。只能用传值返回。
2.出了作用域且返回的变量仍存在,才可以用引用返回。
所以返回的正确使用方法:1.值传递
//返回的正确玩法1:
int Count5() {
int n = 0;
cout << &n<< endl;
n++;
return n;
}
int main() {
int ret = Count5();
cout << ret << endl;
cout << &ret << endl;
return 0;
}
运行结果:
返回的正确使用方法:2.引用返回
int& Count6() {
static int n = 0;
cout << &n << endl;
n++;
return n;
}
int main() {
int ret = Count6();
cout << ret << endl;
cout << &ret << endl;
return 0;
}
运行结果:
二.引用传递和值传递的效率性能比较
例:创建一个结构体,里面开辟10000个整型元素的数组(相当于开辟了4w字节),我会通过值返回和引用返回去比较他俩执行的速度快慢:
#include<time.h>
#include<iostream>
using namespace std;
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1(){ return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{
// 以值作为函数的返回值类型
size_t begin1 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc1();
size_t end1 = clock();
// 以引用作为函数的返回值类型
size_t begin2 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc2();
size_t end2 = clock();
// 计算两个函数运算完成之后的时间
cout << "TestFunc1 time:" << end1 - begin1 << endl;
cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
int main() {
TestReturnByRefOrValue();
}
运行结果:
从代码调试结果可得:引用返回所消耗的时间为1毫秒,而传值返回所消耗的时间是156毫秒,两者对比下来,引用返回要比值返回效率高很多。