C++函数中返回引用和返回值的区别
一、主要讨论下面两个函数的区别:
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++不保证这种优化一定会做。
C++函数中返回引用和返回值的区别的更多相关文章
- JavaScript 在函数中使用Ajax获取的值作为函数的返回值
解决:JavaScript 在函数中使用Ajax获取的值作为函数的返回值,结果无法获取到返回值 原因:ajax默认使用异步方式,要将异步改为同步方式 案例:通过区域ID,获取该区域下所有的学校 var ...
- Java中没有引用传递只有值传递(在函数中)
◆传参的问题 引用类型(在函数调用中)的传参问题,是一个相当扯的问题.有些书上说是传值,有些书上说是传引用.搞得Java程序员都快成神经分裂了.所以,我们最后来谈一下“引用类型参数传递”的问题. 如下 ...
- js的for循环中出现异步函数,回调引用的循环值始终是最后的值
一.问题 今天工作中解决bug发现是由“for循环的异步函数,回调引用的循环值始终是最后的值”的现象导致的,如: for (var i = 0; i < files.length; i++) { ...
- CSAPP读书随笔之一:为什么汇编器会将call指令中的引用的初始值设置为-4
CSAPP,即<深入理解计算机系统:程序员视角>第三版,是一本好书,但读起来确需要具备相当的基本功.而且,有的表述(中译文)还不太直白. 比如,第463页提到,(对于32位系统)为什么汇编 ...
- 理解Java中的引用传递和值传递
关于Java传参时是引用传递还是值传递,一直是一个讨论比较多的话题,有论坛说Java中只有值传递,也有些地方说引用传递和值传递都存在,比较容易让人迷惑.关于值传递和引用传递其实需要分情况看待,今天学习 ...
- (转载)理解Java中的引用传递和值传递
关于Java传参时是引用传递还是值传递,一直是一个讨论比较多的话题,有论坛说Java中只有值传递,也有些地方说引用传递和值传递都存在,比较容易让人迷惑.关于值传递和引用传递其实需要分情况看待,今天 ...
- Java中的引用传递和值传递
Java中的引用传递和值传递 关于Java的引用传递和值传递,在听了老师讲解后,还是没有弄清楚是怎么一回事,于是查了资料,所以在这里与大家分享,有不对的地方,欢迎大家留言. java中是没有指针的,j ...
- (C/C++学习)21.C++中返回引用和返回对象以及传引用和传对象问题
说明:在学习和编写C++代码时,经常会遇到这样的问题:一个带返回值的函数,到底应该返回值呢,还是应该返回引用呢:在传递参数的时候,是应该传递参数的引用呢,还是应该传值呢?请看下面代码: void my ...
- C#中的引用传递、值传递
先来说下C#中的数据类型.分值类型和引用类型两大类. 值类型:直接存储数据的值,保存在内存中 引用类型:存储对值的引用,实际上存储的就是一个内存的地址 C#预定义的简单类型,像int,float, ...
- <转>关于 error LNK2019:无法解析的外部符号 ,该符号在函数**中被引用的思考
错误提示信息摘抄如下: ---------------------------------------------------------------------------------------- ...
随机推荐
- 20155326《Java程序设计》实验一实验报告
实验内容 1.使用JDK编译.运行简单的Java程序: 2.使用Eclipse 编辑.编译.运行.调试Java程序. 实验要求 1.没有Linux基础的同学建议先学习<Linux基础入门(新版) ...
- mysql_变量
set names gbk; 变量 变量分为两种:系统变量,自定义变量 系统变量:系统定义好的,大部分情况用户不需要使用系统变量,如autocommit,auto_increment_incremen ...
- bootstrap 警告框多个删除
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- hive sql 查询一张表的数据不在另一张表中
有时,我们需要对比两张表的数据,找到在其中一张表,不在另一张表中的数据 hql 如下: SELECT * FROM (SELECT id FROM a WHERE dt = '2019-03-17' ...
- SVN代码管理发布
1.svn的独立模式应用 2.svn钩子的应用(例如:代码提交前的文件格式限制,大小限制,代码发布svn成功后的备份等等) 3.大型企业的代码发布流程 有一些制度流程.逻辑方案 4.业务变更管理
- Cerebro_变量名搜索插件
Cerebro 安装 兼容环境:Windows, MacOS, Linux 插件依赖于 Cerebro,下载地址: https://github.com/KELiON/cerebro/releases ...
- Python自动化开发 - 字符串, 列表, 元组, 字典和和文件操作
一.字符串 特性:字符串本身不可修改,除非字符串变量重新赋值.Python3中所有字符串都是Unicode字符串,支持中文. >>> name = "Jonathan&q ...
- unigui如何连接数据库
unigui如何连接数据库 UNIGUI既可以二层直连数据库,也可以通过中间件连接数据库. 这里只介绍UNIGUI二层直连数据库. 数据库连接控件.数据集控件都要拖放在MainModule窗体上.UN ...
- java 异步机制与同步机制的区别
所谓异步输入输出机制,是指在进行输入输出处理时,不必等到输入输出处理完毕才返回.所以异步的同义语是非阻塞(None Blocking). 网上有很多网友用很通俗的比喻 把同步和异步讲解的很透彻 转过 ...
- spring cloud学习(六) 配置中心-自动更新
上一篇学习了spring cloud config的基本使用,但发现有个问题,就是每次更改配置后,都需要重启服务才能更新配置,这样肯定是不行的.在上网查资料了解后,spring cloud支持通过AM ...