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


    目录

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

    1.值返回

            2.引用返回: 

            当n是全局变量:

            当n是局部变量:

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

     总结:

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

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

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


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

    1.值返回

            当n是全局变量:

    1. int Count1() {
    2. static int n = 0;
    3. n++;
    4. //....
    5. return n;
    6. }
    7. int main() {
    8. int ret = Count1();
    9. cout << ret << endl;
    10. return 0;
    11. }

           调用Count1时会为其建立栈帧, 因为变量n是全局变量,n不在栈区而在静态区,之后Count1函数结束,栈帧被销毁,但n的生命周期是全局的,执行return n时n还在,这样ret就接收到函数的返回值了。

    而当n是局部变量时:

    1. int Count2() {
    2. int n = 0; //局部变量
    3. n++;
    4. //....
    5. return n;
    6. }
    7. int main() {
    8. int ret2 = Count2();
    9. cout << ret2 << endl;
    10. return 0;
    11. }

            因为n是局部变量,当函数即将结束时,n会被销毁,此时,执行return n会让n在栈帧外开辟一个临时变量,里面存放n的值(拷贝),而ret指向Count2的返回值,又会再进行对n的临时变量进行接收(再拷贝一次)。

    所以值传递效率低:会进行两次拷贝,时间效率较低。

    2.引用返回: 

            当n是全局变量:

    1. //引用返回
    2. int& Count3() {
    3. static int n = 0;
    4. n++;
    5. //....
    6. return n;
    7. }
    8. int main() {
    9. int ret = Count3();
    10. cout << ret << endl;
    11. return 0;
    12. }

            用ret接收Count3的引用返回时,n会为其开辟临时变量,但这个变量并不开辟空间,所以整个过程只进行了一次拷贝,效率高。

            当n是局部变量:

    1. int& Count4() {
    2. int n = 0;
    3. n++;
    4. //....
    5. return n;
    6. }
    7. int main() {
    8. int ret = Count4();
    9. cout << ret << endl;
    10. return 0;
    11. }

            n为局部变量,函数栈帧被销毁时,n也没了,所以返回n时,使用临时变量(n的别名)会越界,ret属于非法访问n变量(相当于野指针的访问),虽然不会报错,但是属于非法操作。 

            n被销毁,这块空间还给了操作系统,空间还在,也可以再进行访问,只不过是非法访问而已,但之前存储的数据是不被保护的,空间可能会分配给其他人,那么这块空间原有的数据也会被覆盖。 就好比住酒店房间时,我们放了很多东西在房间,游玩了几天后,退房时将房卡还给了酒店工作人员,离开酒店后才想起当时自己的毛巾落在了酒店,但那个房间已经不属于我了,这时有两种情况:

           一.我偷偷回到房间,毛巾还在,这时房间还未分配给其他人;                                   

     

            二.我回来的时候,房间已经被保洁阿姨打扫过了(门还开着),我悄悄进去发现毛巾已经不在那里了,那里放着一卷纸(被覆盖了数据)。

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

    1. int& Count4() {
    2. int n = 0;
    3. n++;
    4. //....
    5. return n;
    6. }
    7. void Func() {
    8. int x = 100;
    9. }
    10. int main() {
    11. int& ret = Count4();
    12. cout << ret << endl;
    13. Func();
    14. cout << ret << endl;
    15. return 0;
    16. }

    运行结果: 

    为什么在调用Func函数后,ret就变成了100了?

            此时的Func函数中所开辟的栈帧位置就是之前Count4函数的栈帧位置, Count4函数被销毁,里面的整型变量n也还给了操作系统,而后Func开辟栈帧,里面同样也是开辟了一个整型变量,与n同样大小,原来变量n的所处空间被OS复用给了Func函数,其空间原有的数值被变量x所覆盖,那么意味着这块空间属于变量x了,又因为之前ret被定义为是这块空间的别名,所以ret的值也就变成了100。

    1. int& Count4() {
    2. int n = 0;
    3. n++;
    4. //....
    5. cout <<"变量n的地址:"<< & n << endl;
    6. return n;
    7. }
    8. void Func() {
    9. int x = 100;
    10. cout << "变量x的地址:" << &x << endl;
    11. }
    12. int main() {
    13. int& ret = Count4();
    14. cout << ret << endl;
    15. cout <<"ret的地址:"<< & ret << endl; //取ret的地址
    16. cout << endl; //换行
    17. Func();
    18. cout << ret << endl;
    19. cout << "调用Func后ret的地址:" << &ret << endl; //取ret的地址
    20. return 0;
    21. }

     结果:

           我们发现: 变量n的地址与变量x的地址完全一样,而调用各函数后,ret的地址也是相同的,更加印证了之前的推论,所以引用返回不能这样使用,ret的地址不可与函数中变量的地址相同!!!

     总结:

    1.出了作用域,且返回的变量不存在,不可用引用返回,因为引用返回导致的结果是不确定的。只能用传值返回。

    2.出了作用域且返回的变量仍存在,才可以用引用返回。

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

    1. //返回的正确玩法1:
    2. int Count5() {
    3. int n = 0;
    4. cout << &n<< endl;
    5. n++;
    6. return n;
    7. }
    8. int main() {
    9. int ret = Count5();
    10. cout << ret << endl;
    11. cout << &ret << endl;
    12. return 0;
    13. }

    运行结果: 

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

    1. int& Count6() {
    2. static int n = 0;
    3. cout << &n << endl;
    4. n++;
    5. return n;
    6. }
    7. int main() {
    8. int ret = Count6();
    9. cout << ret << endl;
    10. cout << &ret << endl;
    11. return 0;
    12. }

    运行结果:

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

            例:创建一个结构体,里面开辟10000个整型元素的数组(相当于开辟了4w字节),我会通过值返回和引用返回去比较他俩执行的速度快慢:

    1. #include
    2. #include
    3. using namespace std;
    4. struct A { int a[10000]; };
    5. A a;
    6. // 值返回
    7. A TestFunc1(){ return a; }
    8. // 引用返回
    9. A& TestFunc2() { return a; }
    10. void TestReturnByRefOrValue()
    11. {
    12. // 以值作为函数的返回值类型
    13. size_t begin1 = clock();
    14. for (size_t i = 0; i < 100000; ++i)
    15. TestFunc1();
    16. size_t end1 = clock();
    17. // 以引用作为函数的返回值类型
    18. size_t begin2 = clock();
    19. for (size_t i = 0; i < 100000; ++i)
    20. TestFunc2();
    21. size_t end2 = clock();
    22. // 计算两个函数运算完成之后的时间
    23. cout << "TestFunc1 time:" << end1 - begin1 << endl;
    24. cout << "TestFunc2 time:" << end2 - begin2 << endl;
    25. }
    26. int main() {
    27. TestReturnByRefOrValue();
    28. }

    运行结果: 

            从代码调试结果可得:引用返回所消耗的时间为1毫秒,而传值返回所消耗的时间是156毫秒,两者对比下来,引用返回要比值返回效率高很多。 

  • 相关阅读:
    【初阶数据结构】深入解析栈:探索底层逻辑
    WebKit与CSSOM视图模块:探索现代Web布局的协同进化
    苹果系统H5下拉加载事件重复触发(react hooks)
    Python复习笔记3——测试与调试技巧
    express服务器中的cors解决跨域步骤---express后端解决跨域问题的方法cors
    cks 考试指南
    《白帽子讲web安全》
    计算机网路学习-time_wait过多
    我的react面试题笔记整理(附答案)
    大数据之LibrA数据库常见术语(八)
  • 原文地址:https://blog.csdn.net/weixin_69283129/article/details/127749788