c++中返回对象与返回引用的区别

时间:2022-12-12 18:11:30

这几天在做用C++做课程设计,对其返回对象的实现感到迷惑。

通过对汇编代码的分析,可以清楚的看到,直接返回引用和返回对象的区别到底是什么。

分析的程序如下

#include<cstdio>

class Node
{
public:

    Node(int _num, char *_str):
        num(_num), str(_str)
        {

        }

    int getInt()
    {
        return num;
    };

    void setInt(int n)
    {
        num = n;
    }

    char* getStr()
    {
        return str;
    }
private:
    int num;
    char *str;
};

Node node(,"good");

Node getNode1()
{
    return node;
}

Node& getNode2()
{
    return node;
}
int main()
{
    Node node1 = getNode1();
    printf("%d,%s",node1.getInt(),node1.getStr());
    Node node2 = getNode2();
    printf("%d,%s",node2.getInt(),node2.getStr());
    ;
}

有两个getNode函数,一个直接返回对象,一个返回对象的引用。

反汇编结果(采用MinGW编译器)

. ; Node getNode1()
.                 public __Z8getNode1v
. __Z8getNode1v   proc near               ; CODE XREF: _main+Fp
.                 push    ebp
.                 mov     ebp, esp
.                 mov     eax, ds:_node.num ;将成员num的值放到eax寄存器
.                 mov     edx, ds:_node.str ;将成员str的值放到eda寄存器
.text:0040157E                 pop     ebp
.text:0040157F                 retn
.text:0040157F __Z8getNode1v   endp
.text:0040157F
.
. ; =============== S U B R O U T I N E =======================================
.
. ; Attributes: bp-based frame
.
. ; Node *getNode2()
.                 public __Z8getNode2v
. __Z8getNode2v   proc near               ; CODE XREF: _main+48p
.                 push    ebp
.                 mov     ebp, esp
.                 mov     eax, offset _node ;将node对象的地址值放到eax寄存器
.                 pop     ebp
.                 retn
. __Z8getNode2v   endp
.
.text:0040158A
.text:0040158A ; =============== S U B R O U T I N E =======================================
.text:0040158A
.text:0040158A ; Attributes: bp-based frame
.text:0040158A
.text:0040158A ; int main()
.text:0040158A                 public _main
.text:0040158A _main           proc near               ; CODE XREF: ___tmainCRTStartup+25Dp
.text:0040158A
.
.text:0040158A
.text:0040158A                 push    ebp
.text:0040158B                 mov     ebp, esp
.text:0040158D                 push    ebx
.text:0040158E                 and     esp, 0FFFFFFF0h
.                 sub     esp, 20h        ;开辟栈空间
.                 call    ___main
.                 call    __Z8getNode1v   ; getNode1(void)
.text:0040159E                 mov     [esp+18h], eax  ;取出成员num的值放到栈中
.text:004015A2                 mov     [esp+1Ch], edx  ;去除成员str的值放到栈中
.text:004015A6                 lea     eax, [esp+18h]  ;栈中对象的首地址存取eax寄存器,当作.text:004015AC处函数调用的参数
.text:004015AA                 mov     ecx, eax
.text:004015AC                 call    __ZN4Node6getStrEv ; Node::getStr(void)
.text:004015B1                 mov     ebx, eax
.text:004015B3                 lea     eax, [esp+18h]  ;栈中对象的首地址存取eax寄存器,当作.text:004015B9处函数调用的参数
.text:004015B7                 mov     ecx, eax
.text:004015B9                 call    __ZN4Node6getIntEv ; Node::getInt(void)
.], ebx
.], eax
.text:004015C6                 mov     dword ptr [esp], offset __format ; this
.text:004015CD                 call    __Z6printfPKcz  ; printf(char const*,...)
.text:004015D2                 call    __Z8getNode2v   ; getNode2(void)
.]    ;返回值为Node对象的首地址,存放在eax寄存器中,此处取其成员str的值
.text:004015DA                 mov     eax, [eax]      ;返回值为Node对象的首地址,存放在eax寄存器中,此处取其成员num的值
.text:004015DC                 mov     [esp+10h], eax  ;将Node对象首地址放到栈中(esp+10h处)
.text:004015E0                 mov     [esp+14h], edx
.text:004015E4                 lea     eax, [esp+10h]  ;将栈中esp+10h处存放的Node对象首地址放到eax中,为函数调用的参数
.text:004015E8                 mov     ecx, eax
.text:004015EA                 call    __ZN4Node6getStrEv ; Node::getStr(void)
.text:004015EF                 mov     ebx, eax        ;返回的字符串首地址存在eax寄存器中,此处复制到ebx寄存器中
.text:004015F1                 lea     eax, [esp+10h]  ;将栈中esp+10h处存放的Node对象首地址放到eax中,为函数调用的参数
.text:004015F5                 mov     ecx, eax
.text:004015F7                 call    __ZN4Node6getIntEv ; Node::getInt(void)
.], ebx
.                 ], eax
.                 mov     dword ptr [esp], offset __format ; __format
.text:0040160B                 call    __Z6printfPKcz  ; printf(char const*,...) 此函数参数用栈传递 esp+8为字符串首地址 esp+4为num
.
.                 mov     ebx, [ebp+var_4]
.                 leave
.                 retn
. _main           endp

在汇编代码中,可以清楚的看到函数返回引用实际是返回的一个指针,直接返回对象则会把对象中的数据全部复制(此处只有一个int和一个char指针,所以用了两个寄存器,如果数据项很多则会使用栈传递)。

Node node2 = getNode2(); //此函数返回对象引用

而对于上边这句,由于node2为一个存在栈中的对象,所以会根据getNode1函数返回的指针将其指向对象所有的数据全部拷贝到栈中对象的空间内。

对于

Node node1 = getNode1();//此函数直接返回对象

是将此函数的返回数据全部拷贝到栈中,返回的数据包含原对象的所有数据。

综上

c++中返回对象引用实际是返回的对象指针,直接返回对象会把对象的数据全部拷贝一遍(拷贝到寄存器或栈中)。

而对于将返回值赋值给一个对象时,如果函数返回的是引用,则会根据指针将所有数据拷贝过去,如果返回的是对象,会把返回的对象的数据(可能在寄存器中,或栈中)再拷贝到需赋值的对象中去。

所以

Node node = getNode()

如果函数返回对象引用,对象的数据会被拷贝一次,如果直接返回对象,则会被拷贝两次。

c++中返回对象与返回引用的区别的更多相关文章

  1. C&plus;&plus;返回对象和返回引用

    我们发现,在C++中,有些成员函数返回的是对象,而有些函数返回的又是引用. 返回对象和返回引用的最主要的区别就是函数原型和函数头. Car run(const Car &)     //返回对 ...

  2. &lbrack;转&rsqb;ThinkPHP中实例化对象M&lpar;&rpar;和D&lpar;&rpar;的区别,select和find的区别

    1.ThinkPHP中实例化对象M()和D()的区别 在实例化的过程中,经常使用D方法和M方法,这两个方法的区别在于M方法实例化模型无需用户为每个数据表定义模型类,如果D方法没有找到定义的模型类,则会 ...

  3. ThinkPHP中实例化对象M&lpar;&rpar;和D&lpar;&rpar;的区别,select和find的区别

    原文:ThinkPHP中实例化对象M()和D()的区别,select和find的区别 1.ThinkPHP中实例化对象M()和D()的区别 在实例化的过程中,经常使用D方法和M方法,这两个方法的区别在 ...

  4. ThinkPHP中实例化对象M&lpar;&rpar;和D&lpar;&rpar;的区别

    ThinkPHP中实例化对象M()和D()的区别 ThinkPHP中实例化对象M()和D()的区别?ThinkPHP如何实例化对象?在实例化的过程中,经常使用D方法和M方法,这两个方法的区别在于M方法 ...

  5. C&num;中Monitor对象与Lock关键字的区别分析

    这篇文章主要介绍了C#中Monitor对象与Lock关键字的区别,需要的朋友可以参考下 Monitor对象 1.Monitor.Enter(object)方法是获取 锁,Monitor.Exit(ob ...

  6. &lpar;C&sol;C&plus;&plus;学习&rpar;21&period;C&plus;&plus;中返回引用和返回对象以及传引用和传对象问题

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

  7. &lbrack;转&rsqb; C&plus;&plus;中临时对象及返回值优化

    http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...

  8. 转:C&plus;&plus;中临时对象及返回值优化

    http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...

  9. Vue中data返回对象和返回值的区别

    速记:粗浅的理解是,事件的结果是影响单个组件还是多个组件.因为大部分组件是要共享的,但他们的data是私有的,所以每个组件都要return一个新的data对象 返回对象的时候 <!DOCTYPE ...

随机推荐

  1. MySql学习(MariaDb)

    资料 http://www.cnblogs.com/lyhabc/p/3691555.html http://www.cnblogs.com/lyhabc/p/3691555.html MariaDb ...

  2. LINQ to SQL语句&lpar;2&rpar;之Select&sol;Distinct

    适用场景:o(∩_∩)o- 查询呗. 说明:和SQL命令中的select作用相似但位置不同,查询表达式中的select及所接子句是放在表达式最后并把子句中的变量也就是结果返回回来:延迟.Select/ ...

  3. Oracle 数值函数

    上一次整理了一下Oracle字符串中常用的函数,接下来就整理一下Oracle数值方面的一些常用的函数. 1.NVL 空值转换函数,请注意一下,任何包含NULL值的算术运算都会得到NULL,这个函数有点 ...

  4. How to install Node&period;js on Linux

    How to install Node.js on Linux Posted on November 13, 2015 by Dan Nanni Leave a comment Question: H ...

  5. Unity5 打assetbundle时,无法打成图集仍然是散图的原因

    首先需要把 SpritePacker设置成可用 菜单Edit->project setting->editor      修改为可用 然后,保证所有图片的导入格式如下 最主要是 packi ...

  6. DM 9000网卡驱动移植

    1. 由于内核已经带有DM9000 网卡的驱动,所以主要移植工作是在板文件中添加 platform_device 结构,并加入 ok6410_devices[] __initdata 数组. 代码如下 ...

  7. Working With JSON

    JavaScript对象表示法(JSON)是用于将结构化数据表示为JavaScript对象的标准格式,通常用于在网站上表示和传输数据(例如从服务器向客户端发送一些数据,因此可以将其显示在网页上). J ...

  8. Vuex以及axios 看这个

      vuex  -- 安装   npm i vuex  -- 配置   -- 导入vuex      import vuex from "vuex"   -- vue使用vuex  ...

  9. Tomcat 总体结构

    一.Tomcat 总体结构 1.Server(服务器)是Tomcat构成的*构成元素,所有一切均包含在Server中,Server的实现类StandardServer可以包含一个到多个Service ...

  10. BZOJ2221&colon; &lbrack;Jsoi2009&rsqb;面试的考验

    传送门 一句话题意,给定一个序列,询问区间内差值的绝对值的最小值. 这道题之前见过一次,似乎是在一次UER上,那一道题当时是用了近似算法才能过. 数据保证数列随机. 这道题显然非常适合离线的做法,考虑 ...