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

2023-11-12

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

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)。

 

所以结论就是:

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++不保证这种优化一定会做。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • equals与==的区别

    1 equals 函数 只是用来比较两个变量中的值是否一致 它不管类型等要素 例s1 ssss s2 ssss s1 equals s2 为ture 2 是比较两个变量的地址是否一样 类型不一样地址也不一样 就算类型一样 储存的空间不一样的
  • BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Mode

    Paper name BLIP 2 Bootstrapping Language Image Pre training with Frozen Image Encoders and Large Language Models Paper R
  • control的用法及短语_out of control用法及例句

    out of control短语的意思是 失去控制 不受控制 如果Out of control 三个单词之间用横杠连接 做形容词 意思是失控的 out of control用法例句 An out of control car crashed
  • IDEA配置sonar

    idea配置sonar步骤如下 1 安装sonar插件 如果安装失败就多安装几次 博主就是安装了两次 第一次安装失败了 第二次才安装成功的 安装成功后重启idea 2 添加sonar服务器连接 3 选择连接到服务 填入名称和服务地址 4 n
  • window 设置开机自启脚本

    想在开机的时候启动某些程序 exe bat 什么的 想启动那个程序找到程序的启动目录 或者右键其快捷方式 打开文件位置找到其应用程序 如果路径为 D weixin wechat exe 则新建一个文件 文本类型就行 打开后编辑 D cd w
  • 吐血总结!50道Python面试题集锦(附答案)

    Python是目前编程领域最受欢迎的语言 在本文中 我将总结Python面试中最常见的50个问题 每道题都提供参考答案 希望能够帮助你在2023年求职面试中脱颖而出 找到一份高薪工作 这些面试题涉及Python基础知识 Python编程 数
  • 类的成员函数

    类的成员函数 1 拷贝构造函数 1 1 定义 1 2 特征 1 3 implementation 2 赋值运算符重载 2 1 定义 2 2 特征 2 3 implementation 1 拷贝构造函数 1 1 定义 构造函数 特殊的成员函数
  • 怎么在gitLab代码拉到本地

    怎么在gitLab代码拉到本地 新手从gitLab下载代码到本地 首先在本地上面创建一个空文件 然后在cmd里边输入 命令 1 cd C Users l Desktop 帝测科技 dou 创建的空文件路径 2 git clone http
  • amd 虚拟化怎么开_虚拟化技术 - CPU虚拟化

    物理机器是由CPU 内存和I O设备等一组资源构成的实体 虚拟机也一样 由虚拟CPU 虚拟内存和虚拟I O设备等组成 VMM VM Monitor 按照与传统OS并发执行用户进程的相似方式 仲裁对所有共享资源的访问 前面的文章对虚拟化技术进
  • windows下:“activate” is not a conda command

    问题描述 在windows下安装了anaconda并安装虚拟环境后 打开Anaconda Prompt窗口 执行 conda activate virtual env 时出现以下错误 CommandNotFoundError Command
  • 神州路由器DHCPv6中继服务配置

    配置如下 R1 Router gt ena Router conf R1 config ipv6 unicast routing R1 config int g0 1 R1 config g0 1 ipv6 add 2001 1 2 64
  • 在WPF中将数据从数据库导出到Excel的快速解决方案

    Spire XLS一种专业且功能强大的Excel API 使开发人员 程序员可以使用其应用程序操作Excel文件 没有Microsoft Automation Spire XLS可使编程变得更加简单 免费下载 本文将介绍一种将数据从数据库导
  • python增删改查界面_必看!!!python列表( 增 删 改 查),超详细讲解!!

    列表类型特有方法 不同的类型有自己不同的操作方法 对列表的操作有增删改查四种操作 1 增加操作 方法描述append追加 在列表的尾部加入指定的元素 extend将指定序列的元素依次追加到列表的尾部 合并 不会去重复内容 insert将指定
  • 身份证性别判断

    性别判断 param value return 1 女 2 男 public String execute String value value value trim if value length 15 if Integer parseI
  • 加速度计和陀螺仪指南

    本帖翻译自IMU 加速度计和陀螺仪设备 在嵌入式应用中使用的指南 这篇文章主要介绍加速度计和陀螺仪的数学模型和基本算法 以及如何融合这两者 侧重算法 思想的讨论 介绍 本指南旨在向兴趣者介绍惯性MEMS 微机电系统 传感器 特别是加速度计和
  • ReentrantLock详解

    一 AQS 1 AbstractQueuedSynchronizer AbstractQueuedSynchronizer简称AQS 是一个用于构建锁和相关同步器的框架 它依赖于FIFO的等待队列实现 见AbstractQueuedSync
  • FS技术总结

    技术总结 1 单机FIO测试 1 1 配置FS环境 1 2 配置 Linux NVMe over Fabrics 主机 1 3 FIO通过配置文件运行 1 4 双机 双fio测试 1 5 fio测试 2 优化代码 2 1 程序运行细节 3
  • 【JavaEE】多线程CAS中的aba问题是什么?

    博主简介 想进大厂的打工人 博主主页 xyk 所属专栏 JavaEE初阶 什么是CAS问题 CAS 全称Compare and swap 字面意思 比较并交换 CAS中的aba问题是什么 请看本文讲解 目录 文章目录 一 CAS是什么 二
  • Qt 串口调试助手16进制收发显示

    就不废话了 直接贴源码 如果你看懂我的源码的画 我认为90 的概率能解决你16进制显示问题 注意 注意 注意 qt低版本可能不提供arr hex 这个函数 源码 QString str ui gt lineEdit gt text 从QLi
  • C++函数中返回引用和返回值的区别

    一 主要讨论下面两个函数的区别 int at return m data int at return m data 上面两个函数 第一个返回值是int的引用int 第二个返回值是int 二者的区别是什么呢 我们先用一个语句 const in