C++基础——引用讲解2

目录

一.引用   返回和值返回的区别

1.值返回

        2.引用返回: 

        当n是全局变量:

        当n是局部变量:

根据解释,模拟实现上面这个说的酒店案例:

 总结:

所以返回的正确使用方法:1.值传递

 返回的正确使用方法:2.引用返回

二.引用传递和值传递的效率性能比较


一.引用   返回和值返回的区别

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毫秒,两者对比下来,引用返回要比值返回效率高很多。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙予清的zzz~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值