这几天在做用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. C# 数据结构 线性表(顺序表 链表 IList 数组)

    线性表 线性表是最简单.最基本.最常用的数据结构.数据元素 1 对 1的关系,这种关系是位置关系. 特点 (1)第一个元素和最后一个元素前后是没有数据元素,线性表中剩下的元素是近邻的,前后都有元素. ...

  2. 为何要fork()两次来避免产生僵尸进程??

    最近安装书上说的,开始搞多进程了..看到了一个好帖子,学习学习 http://blog.sina.com.cn/s/blog_9f1496990100y420.html 首先我们要明白,为什么要避免僵 ...

  3. TMS IntraWeb 5.4.1.1 for XE6 (适配Intraweb14.0.32)

    文件夹内含有我自己已经编译好的bpl,仅供大家学习使用,请支持正版!!导入ParaInstalarXE6.groupproj后,逐个编译安装即可. 链接:http://pan.baidu.com/s/ ...

  4. 自动生成makefile的脚本

    如果需要测试某一个特性,写了一个test.cpp 某天又增加了一个utils.cpp,依此类推,测试文件越来越多 每次测试时都要手动维护一个makefile实在是不明智的 于是萌生了用脚本自动维护的念 ...

  5. golang_protobuf环境搭建

    搭建golang使用rotobuf使用环境 一 安装protobuf: 1 下载protobuf源码:https://github.com/google/protobuf 2 进入源码目录: ./au ...

  6. strong ,weak

    有时我们写个代码开源出来给别人用时,会被其他开发者抱怨编译不了,很多情况是版本的问题,尤其现在ARC的出现后关于weak,strong的问题让人头疼.有个开源代码这里做的很不错,就是MBProgres ...

  7. Ecshop开发

    http://www.cnblogs.com/xcxc/category/579565.html

  8. 【HDOJ】3789 奥运排序问题

    写了个函数指针,这题目很水,但是佷烦. #include <iostream> #include <cstring> #include <cstdio> #incl ...

  9. js之script属性async与defer

    概念 默认情况下js的脚本执行是同步和阻塞的,但是 <script> 标签有 defer 和 async 属性, 这可以改变脚本的执行方式,这些都是布尔类型了,没有值,只需要出现在 < ...

  10. (转载)用PHP实现翻页

    (转载)http://blog.csdn.net/emili/article/details/5221744 原文参考http://www.cnblogs.com/xxcainiao/archive/ ...