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

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

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

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

反汇编结果:

 #int& at()
#{
# return m_data_;
#} 00BB6830 push ebp
00BB6831 mov ebp,esp
00BB6833 sub esp,0CCh
00BB6839 push ebx
00BB683A push esi
00BB683B push edi
00BB683C push ecx
00BB683D lea edi,[ebp-0CCh]
00BB6843 mov ecx,33h
00BB6848 mov eax,0CCCCCCCCh
00BB684D rep stos dword ptr es:[edi]
00BB684F pop ecx
00BB6850 mov dword ptr [this],ecx
m_data_++;
00BB6853 mov eax,dword ptr [this]
00BB6856 mov ecx,dword ptr [eax]
00BB6858 add ecx,
00BB685B mov edx,dword ptr [this]
00BB685E mov dword ptr [edx],ecx
return m_data_;
#取地址this中的值5879712(m_data_的地址)到寄存器eax中,此时寄存器eax存的是m_data_的地址
00BB6860 mov eax,dword ptr [this]
}
00BB6863 pop edi
00BB6864 pop esi
00BB6865 pop ebx
00BB6866 mov esp,ebp
00BB6868 pop ebp
00BB6869 ret const int& a = mymay.at();
00176AA2 lea ecx,[mymay]
00176AA5 call MyMat::at (0171546h)
#此时寄存器eax中的值为m_data_的地址5879712,直接将地址5879712存入地址a中。
00176AAA mov dword ptr [a],eax
cout << a << endl;
 #int at()
#{
# return m_data_;
#} 012B6830 push ebp
012B6831 mov ebp,esp
012B6833 sub esp,0CCh
012B6839 push ebx
012B683A push esi
012B683B push edi
012B683C push ecx
012B683D lea edi,[ebp-0CCh]
012B6843 mov ecx,33h
012B6848 mov eax,0CCCCCCCCh
012B684D rep stos dword ptr es:[edi]
012B684F pop ecx
012B6850 mov dword ptr [this],ecx
return m_data_;
#和上面一样,也是先取出m_data_的地址
012B6853 mov eax,dword ptr [this]
#和上面不一样,不是直接将m_data_的地址放入寄存器eax中,而是取地址5879712中的值(m_data_=)放入寄存器eax中,此时寄存器eax存的是m_data_的值()
012B6856 mov eax,dword ptr [eax]
}
012B6858 pop edi
012B6859 pop esi
012B685A pop ebx
012B685B mov esp,ebp
012B685D pop ebp
012B685E ret const int& a = mymay.at();
008E6AA2 lea ecx,[mymay]
008E6AA5 call MyMat::at (08E154Bh)
#此时eax的值为3,将3存入地址ebp-24h中,
008E6AAA mov dword ptr [ebp-24h],eax
#将eax的值变成ebp-24h
008E6AAD lea eax,[ebp-24h]
#将地址ebp-24h写到地址为a中,此时a代表的地址是ebp-24h
008E6AB0 mov dword ptr [a],eax
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,所以一定会有临时变量产生。

 T function1(){
T t();
return t;
}
T x=function1();

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

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

 T function2(){
return T();
}
T x=function2();

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

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

最新文章

  1. 关于ubuntu16无线网卡RTL8723BE频繁掉线及信号不足的解决办法
  2. CSS伪类选择器
  3. Hack语言特性之类型化
  4. Apache Shiro 使用手册(三)Shiro 授权
  5. spring 注解的总结
  6. 49. Anagrams
  7. IT项目量化管理:细化、量化与图形化 与 中国IT项目实施困惑
  8. Android开源项目发现---Menu 篇(持续更新)
  9. c++中的vector原理
  10. unity3d脚本
  11. Python 基于学习 网络小爬虫
  12. 现在,以编程方式在 Electron 中上传文件,是非常简单的!
  13. 再起航,我的学习笔记之JavaScript设计模式19(状态模式)
  14. 2、jQuery的一些静态方法
  15. Python 编写一个有道翻译的 workflow 教程
  16. Git使用九:合并和删除分支
  17. SSH集成(Struts+Spring+Hibernate)
  18. SpringCloud使用jpa之Rest方式
  19. 【C#】Queue的简单试用
  20. tar 打包带软连接的文件

热门文章

  1. noip第33课作业
  2. ASP.NET Web API 框架研究 Controller实例的销毁
  3. GetFileOpenName()、GetFilesavename
  4. Android-Kotlin-具名参数
  5. Swagger ui测试中的验证 apikey
  6. bootstrap基础学习小记(一)简介模板、全局样式
  7. 轻量级Config文件AppSettings节点编辑帮助类
  8. 关于ASP.NET MVC4在IIS6中不认识安卓移动设备的解决办法
  9. 背水一战 Windows 10 (62) - 控件(媒体类): InkCanvas 保存和加载, 手写识别
  10. OSLab文件描述符