这几天在做用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++返回对象和返回引用

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

  2. [转]ThinkPHP中实例化对象M()和D()的区别,select和find的区别

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

  3. ThinkPHP中实例化对象M()和D()的区别,select和find的区别

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

  4. ThinkPHP中实例化对象M()和D()的区别

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

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

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

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

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

  7. [转] C++中临时对象及返回值优化

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

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

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

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

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

随机推荐

  1. mysql申请账户

    INSERT INTO mysql.user set Host='%',user='alipay',password=password('alipay'),Select_priv='Y',Insert ...

  2. git 创建多个账户ssh

    创建一个账户 创建ssh本地秘钥. $ ssh-keygen -t rsa -C "youremail@xxx.com" 一路回车,会在~/.ssh/目录下生成id_rsa和id_ ...

  3. PS证件照换背景

    综述 博主原创内容. 在PS里,对于抠图,比较有技术含量的便是抠头发丝了,下面为大家带来一个比较详细的抠头发丝的教程. 素材准备 在这里我们用这张图片作为抠图素材,下面让我们一步步来演示抠图的过程,并 ...

  4. 常见HTTP状态码大全

    我们经常会遇到404.500.302等提示,它们究竟是什么意思呢?除了这几个常见的状态码外,还有哪些我们没有遇到过的但有可能出现的状态码呢?网站的http状态对于网站维护人员来说是相当重要的,当网站出 ...

  5. Elasticsearch客户端大全 http://www.searchtech.pro/elasticsearch-clients

    Elasticsearch有各种语言的客户端,下面一一列出来: Perl ElasticSearch.pm: Perl客户端 Python pyes: Python客户端pyelasticsearch ...

  6. Codeforces 713 C Sonya and Problem Wihtout a Legend

    Description Sonya was unable to think of a story for this problem, so here comes the formal descript ...

  7. Git超级菜鸟学习--> 社区化协作以及分支操作

    1 先将主库的url添加到本地上面, git remote add <RepoName> <URL> 2 获取主库的修改记录 git fetch --获取服务器上的修改 git ...

  8. wcf资料

    WCF服务安全控制之netTcpBinding的用户名密码验证http://www.cnblogs.com/wengyuli/archive/2011/05/14/wcf-nettcpbinding- ...

  9. “ORA-12545: 因目标主机或对象不存在,连接失败”怎么办?

    大概知道是因为主机名或者IP地址的原因引起的,但是不知道究竟,就去百度上查了查,然后就根据几种答案做出了以下就该: 1.E:\oracle\product\10.2.0\db_1\NETWORK\AD ...

  10. Linux下如何挂载FAT32格式USB设备

    挂u盘之前,运行命令cat /proc/partitions,看看现在系统中有哪些分区.插上u盘以后,再次运行上述命令,看看多出来什么分区.通常是sda1. 1.插入U盘 2.输入 fdisk -l ...