这几天在做用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. 监视/etc/passwd文件是否正常

    帮助监视/etc/passwd文件是否正常(P90 练习6.7) 1)找出有UID0的所有项 2)找出有重复UID的所有项 3)找出有重复登录名的所有项 4)找出没有口令的所有项 5)找出没有作废日期 ...

  2. Python中使用中文

    python的中文问题一直是困扰新手的头疼问题,这篇文章将给你详细地讲解一下这方面的知识.当然,几乎可以确定的是,在将来的版本中,python会彻底解决此问题,不用我们这么麻烦了. 先来看看pytho ...

  3. Word 2016 test

    Word 2016 test    

  4. 关于applicationx/www-form-urlencoded和multipart/form-data的描述

    在Form元素的语法中,EncType表明提交数据的格式 用 Enctype 属性指定将数据回发到服务器时浏览器使用的编码类型. 下边是说明: application/x-www-form-urlen ...

  5. java特点

    简单: Java语言的语法与C语言和C++语言很接近,使得大多数程序员很容易学习和使用.另一方面,Java丢弃了C++中很少使用的.很难理解的.令人迷惑的那些特性,如操作符重载.多继承.自动的强制类型 ...

  6. AVR GCC对端口的操作指南

    1. AVR GCC for AVR I.I/O端口API1. BV用法:BV(pos);说明:将位定义转换成屏蔽码(MASK).与头文件io.h里的位定义一起使用.例如,置位WDTOE和WDE可表示 ...

  7. apache pdfbox

    转 http://www.blogjava.net/sxyx2008/archive/2010/07/23/326890.html 轻松使用apache pdfbox将pdf文件生成图 近期在项目中使 ...

  8. ios7新特性3-Map Kit新特性

    Map Kit 框架 (MapKit.framework) 包含了大量的改进以及为基于地图的程序提供了新特性.利用地图显示位置信息的应用现在可以使用Maps这个程序用到的3D地图,包括控制程序控制视线 ...

  9. appFramework在三星某些机型上的兼容问题

    有个问题困扰了安卓哥好几天 一个带有fixed抬头的列表页,在向上swipe的时候,有一定概率会把整个画面滚动上去,就连fixed的部分也移动了. 于是哥觉得是webview的问题,找了各种选项,禁用 ...

  10. Java语言基础(二) Java关键字

    Java语言基础(二) Java关键字 Java关键字比较多,我就不列举出来了,只记录一些常用的小知识点: ①Java的关键字只有小写. ②then.sizeof都不是Java的关键字,熟悉C++的程 ...