转自https://www.cnblogs.com/JMLiu/p/7928425.html

一、主要讨论下面两个函数的区别:

int& at()
{
return m_data_;
}
int at()
{
return m_data_;
}

上面两个函数,第一个返回值是int的引用int&,第二个返回值是int,二者的区别是什么呢?

我们先用一个语句 const int& a = mymay.at(); 来分别调用一次上面两个函数,然后看汇编语言的结果。

反汇编结果:

 1 #int& at()
2 #{
3 # return m_data_;
4 #}
5
6 00BB6830 push ebp
7 00BB6831 mov ebp,esp
8 00BB6833 sub esp,0CCh
9 00BB6839 push ebx
10 00BB683A push esi
11 00BB683B push edi
12 00BB683C push ecx
13 00BB683D lea edi,[ebp-0CCh]
14 00BB6843 mov ecx,33h
15 00BB6848 mov eax,0CCCCCCCCh
16 00BB684D rep stos dword ptr es:[edi]
17 00BB684F pop ecx
18 00BB6850 mov dword ptr [this],ecx
19 m_data_++;
20 00BB6853 mov eax,dword ptr [this]
21 00BB6856 mov ecx,dword ptr [eax]
22 00BB6858 add ecx,1
23 00BB685B mov edx,dword ptr [this]
24 00BB685E mov dword ptr [edx],ecx
25 return m_data_;
26 #取地址this中的值5879712(m_data_的地址)到寄存器eax中,此时寄存器eax存的是m_data_的地址
27 00BB6860 mov eax,dword ptr [this]
28 }
29 00BB6863 pop edi
30 00BB6864 pop esi
31 00BB6865 pop ebx
32 00BB6866 mov esp,ebp
33 00BB6868 pop ebp
34 00BB6869 ret
35
36
37
38
39
40 const int& a = mymay.at();
41 00176AA2 lea ecx,[mymay]
42 00176AA5 call MyMat::at (0171546h)
43 #此时寄存器eax中的值为m_data_的地址5879712,直接将地址5879712存入地址a中。
44 00176AAA mov dword ptr [a],eax
45 cout << a << endl;
 1 #int at()
2 #{
3 # return m_data_;
4 #}
5
6
7 012B6830 push ebp
8 012B6831 mov ebp,esp
9 012B6833 sub esp,0CCh
10 012B6839 push ebx
11 012B683A push esi
12 012B683B push edi
13 012B683C push ecx
14 012B683D lea edi,[ebp-0CCh]
15 012B6843 mov ecx,33h
16 012B6848 mov eax,0CCCCCCCCh
17 012B684D rep stos dword ptr es:[edi]
18 012B684F pop ecx
19 012B6850 mov dword ptr [this],ecx
20 return m_data_;
21 #和上面一样,也是先取出m_data_的地址
22 012B6853 mov eax,dword ptr [this]
23 #和上面不一样,不是直接将m_data_的地址放入寄存器eax中,而是取地址5879712中的值(m_data_=3)放入寄存器eax中,此时寄存器eax存的是m_data_的值(3)
24 012B6856 mov eax,dword ptr [eax]
25 }
26 012B6858 pop edi
27 012B6859 pop esi
28 012B685A pop ebx
29 012B685B mov esp,ebp
30 012B685D pop ebp
31 012B685E ret
32
33
34
35
36
37 const int& a = mymay.at();
38 008E6AA2 lea ecx,[mymay]
39 008E6AA5 call MyMat::at (08E154Bh)
40 #此时eax的值为3,将3存入地址ebp-24h中,
41 008E6AAA mov dword ptr [ebp-24h],eax
42 #将eax的值变成ebp-24h
43 008E6AAD lea eax,[ebp-24h]
44 #将地址ebp-24h写到地址为a中,此时a代表的地址是ebp-24h
45 008E6AB0 mov dword ptr [a],eax
46 cout << a << endl;

所以结论就是:

1、返回值为引用型(int& )的时候,返回的是地址,因为这里用的是 int& a=mymay.at(); ,所以a和m_data_指的是同一块地址(由寄存器eax传回的5879712)。

2、返回值不是引用型(int)的时候,返回的是一个数值。这个时候就很有意思了,编译器是先将这个数值放入一个内存中(上面例子中,该内存地址为ebp-24h),再把这个地址付给a,此时的a代表的地址是ebp-24h,和m_data_代表的地址不一样(m_data_代表的地址是5879712)。

3、综上两点可以看出,当返回的值不是引用型时,编译器会专门给返回值分配出一块内存的(例子中为ebp-24h)。

二、说明一下函数返回时,如果不是返回一个变量的引用,则一定会生成一个临时变量。

看下面的函数,返回的是t而不是&t,所以一定会有临时变量产生。

1 T function1(){
2 T t(0);
3 return t;
4 }
5 T x=function1();

这里的过程是:
1.创建命名对象t
2.拷贝构造一个无名的临时对象,并返回这个临时对象
3.由临时对象拷贝构造对象x
4.T x=function1();这句语句结束时,析构临时对象
这里一共生成了3个对象,一个命名对象t,一个临时对象作为返回值,一个命名对象x。

下面的函数稍微复杂一定,它没有先定义一个中间变量t,看起来似乎是直接返回了一个临时变量。但实际上,如果不经过c++的优化,那么它并没有提高效率,因为它还是创建了3个对象。

1 T function2(){
2 return T(0);
3 }
4 T x=function2();

这里的过程是:
1.创建一个无名对象
2.由无名对象拷贝构造一个无名的临时对象
3.析构无名对象,返回临时对象
4.由临时对象拷贝构造对象x
5.T x=function2()语句结束时,析构临时对象。
这里一共生成了3个对象,其中有2个对象都是马上被析构掉的,不能被后面的代码使用。既然是这样,那么就会有优化的余地,可以尝试着不要前面的两个临时变量。c++确实会做这样的优化,优化后的c++会避免匿名对象和临时对象这两个对象的生成,而直接生成x,这样就减少了两次对象生成-回收的消耗,提高了程序性能。

其实function1()这段代码也是会经过优化的,但因为临时对象t是一个命名对象,所以一定会被创建。存储返回值的临时对象是多余的,会被优化掉而不生成。
但是,程序员不应该依赖这种优化,因为c++不保证这种优化一定会做。

C++中返回引用和返回值的区别的更多相关文章

  1. CSAPP读书随笔之一:为什么汇编器会将call指令中的引用的初始值设置为-4

    CSAPP,即<深入理解计算机系统:程序员视角>第三版,是一本好书,但读起来确需要具备相当的基本功.而且,有的表述(中译文)还不太直白. 比如,第463页提到,(对于32位系统)为什么汇编 ...

  2. 理解Java中的引用传递和值传递

    关于Java传参时是引用传递还是值传递,一直是一个讨论比较多的话题,有论坛说Java中只有值传递,也有些地方说引用传递和值传递都存在,比较容易让人迷惑.关于值传递和引用传递其实需要分情况看待,今天学习 ...

  3. (转载)理解Java中的引用传递和值传递

      关于Java传参时是引用传递还是值传递,一直是一个讨论比较多的话题,有论坛说Java中只有值传递,也有些地方说引用传递和值传递都存在,比较容易让人迷惑.关于值传递和引用传递其实需要分情况看待,今天 ...

  4. Java中的引用传递和值传递

    Java中的引用传递和值传递 关于Java的引用传递和值传递,在听了老师讲解后,还是没有弄清楚是怎么一回事,于是查了资料,所以在这里与大家分享,有不对的地方,欢迎大家留言. java中是没有指针的,j ...

  5. Java中没有引用传递只有值传递(在函数中)

    ◆传参的问题 引用类型(在函数调用中)的传参问题,是一个相当扯的问题.有些书上说是传值,有些书上说是传引用.搞得Java程序员都快成神经分裂了.所以,我们最后来谈一下“引用类型参数传递”的问题. 如下 ...

  6. (C/C++学习)21.C++中返回引用和返回对象以及传引用和传对象问题

    说明:在学习和编写C++代码时,经常会遇到这样的问题:一个带返回值的函数,到底应该返回值呢,还是应该返回引用呢:在传递参数的时候,是应该传递参数的引用呢,还是应该传值呢?请看下面代码: void my ...

  7. C#中的引用传递、值传递

      先来说下C#中的数据类型.分值类型和引用类型两大类. 值类型:直接存储数据的值,保存在内存中 引用类型:存储对值的引用,实际上存储的就是一个内存的地址 C#预定义的简单类型,像int,float, ...

  8. C#中的引用传递和值传递。

    最近在写项目中有同事碰到这样的传值问题,可能很多对这个参数的传递还有点疑惑,自己也是对这个基础知识做一个回顾和巩固. 首先什么是值类型和引用类型可以去园里看一下相关的资料,都有介绍. 常用值类型包括: ...

  9. C++函数中返回引用和返回值的区别

    一.主要讨论下面两个函数的区别: int& at() { return m_data_; } int at() { return m_data_; } 上面两个函数,第一个返回值是int的引用 ...

随机推荐

  1. ES之五:ElasticSearch聚合

    前言 说完了ES的索引与检索,接着再介绍一个ES高级功能API – 聚合(Aggregations),聚合功能为ES注入了统计分析的血统,使用户在面对大数据提取统计指标时变得游刃有余.同样的工作,你在 ...

  2. JVM 字节码(一)字节码规范

    JVM 字节码(一)字节码规范 JVM 学习资源 Java ClassFile 字节码规范(Oracle) Java 虚拟机规范(Java SE 7 中文版) (周志明等译) Java 反编译工具 - ...

  3. 重写equals和hashCode的方法

    为什么要有 hashCode引用 我们以"HashSet 如何检查重复"为例子来说明为什么要有 hashCode: 当你把对象加入 HashSet 时,HashSet 会先计算对象 ...

  4. Firefox录制时浏览器提示代理服务器拒绝连接

    解决方法:检查火狐浏览器的代理设置是否正确,在 菜单栏 工具->选项->高级->网络->连接->设置里.将“配置访问因特网的代理”选项改为“无代理”.

  5. Alpha 冲刺 (7/10)

    队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭鸭鸭鸭鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作 学习MSI.CUDA 试运行软件并调试 ...

  6. JavaScript:void(0)使用介绍

    1.点击链接后不做任何事情(为防止点击链接后跳转到页首,onclick事件return false即可) <a href="javascript:void(0);" > ...

  7. easyui中的option设置selected没有效果

    在mvc中,使用了easyUI框架,然后我点击添加新消息的时候弹窗一个新窗口,添加信息.却发现我的select一直无法设置默认状态,jq选中,还是直接在添加一条请选择的option都没办法选中,网络上 ...

  8. mysql 地理位置定位

    SET @pt2 = ST_GeomFromText('POINT(116.405289 39.904987)'); SELECT *,ST_Distance_Sphere(ST_GeomFromTe ...

  9. Python之旅Day3 文件操作 函数(递归|匿名|嵌套|高阶)函数式编程 内置方法

    知识回顾 常见五大数据类型分类小结:数字.字符串.列表.元组.字典 按存值个数区分:容器类型(列表.字典.元组) 标量原子(数字.字符串) 按是否可变区分:可变(列表.字典) 不可变(数字.字符串.元 ...

  10. .NET Core微服务之路:基于Consul最少集群实现服务的注册与发现(二)

    重温Consul最少化集群的搭建