这几天在做用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. Ubuntu系统启动错误问题的解决

    一.hub_port_status failed (err=-110) 1.问题产生的原因 笔者不知道出现这种错误是不是都是相同的原因,但是我的系统出现这种原因是由于: 1.更改了虚拟硬盘的大小和/e ...

  2. POJ 3041 Asteroids 最小点覆盖 == 二分图的最大匹配

    Description Bessie wants to navigate her spaceship through a dangerous asteroid field in the shape o ...

  3. Socket理解

    简介 本文主要介绍的socket编程的实现相关的内容: 理论 函数 socket 用来创建socket描述符,它唯一标识一个socket int socket(int domain, int type ...

  4. Java Fluent Restful API自动化测试框架

    这是一个Restful API自动化测试框架,这是一个能让你写出高可读性测试代码的测试框架! 项目目标 话说目前行业内,Restful API自动化测试框架已经不是稀罕物了,各个语言都有自己的实现机制 ...

  5. 关于.NET的配置文件

    无论是exe文件还是dll文件,都可以添加App.config文件,里面设置配置信息.比如<appSettings></appSettings>之间,可以加入Key-Value ...

  6. Internship

    zoj2532:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1532 题意:有n个发射点,m个中继站,然后发射点会发射一些信息 ...

  7. 【UVALive - 3713】Astronauts (2-SAT)

    题意: 有n个宇航员,按照年龄划分,年龄低于平均年龄的是年轻宇航员,而年龄大于等于平均年龄的是老练的宇航员. 现在要分配他们去A,B,C三个空间站,其中A站只有老练的宇航员才能去,而B站是只有年轻的才 ...

  8. 关于IN-LIST迭代

    IN-list iterator (IN-list迭代) 字段有索引,in里有多少个值就执行多少次索引扫描.不管值是否在字段里存在~ SQL> create table t1 as select ...

  9. 转-----实现基本的Ajax和Json请求

    前面已经封装好了一个方法ajax(),通过这个方法可以实现Ajax请求,接下来就是给出 例程来测试这个方法和实现简单的功能.   视图的部分代码如下: 1 2 3 4 5 6 7 8 9 <bo ...

  10. android获取设备屏幕大小的方法

    // 通过WindowManager获取 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay( ...