/*C++中返回一个对象时的实现及传说中的右值——临时对象*/

如下代码:

 /**********************************************/
class CStudent;
CStudent GetStudent()
{
CStudent loc_stu;
return loc_stu;
} int main()
{
CStudent stu = GetStudent();
}
/**********************************************/

来看一下main函数中调用GetStudent()的汇编实现:

aaarticlea/png;base64,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" alt="" />

看上去,GetStudent()是无参函数,可是为什么在call之前push了一个参数到堆栈里呢?并且call完之后还add esp, 4来使堆栈平衡。

这个调用是这么实现的:
首先,在main函数中,stu是作为局部变量存在的,因为是个对象,会调用构造函数。
但是请注意!!!!:这里并不会在main函数中调用构造函数来构造stu,而是仅仅为stu预留了空间sizeof(CStudent)大小,并将这个空间的地址通过
mov eax, address
push eax
传递给了GetStudent()。

在GetStudent()内部定义了它的局部变量loc_stu,GetStudent()内部构造了它,在返回的时候,通过 <拷贝构造函数> 拷贝到前面提到的那个通过eax传递的隐含的参数。

总结就是说虽然stu在main里面定义,但其内存区域的内容是由GetStudent()完成的。这里需要注意的是这种调用方式,因为返回的是个对象,所以做法是调用者提前准备好一个对象空间,然后把地址告诉被调用者,被调用者得到这个地址后对其赋值,从而完成对象的返回。我姑且将这种调用方式称之为__robjcall:返回对象调用方式!呵呵

注意!!!
如果main函数改成下面这样:

/***************************************************************************/
int main()
{
//CStudent stu = GetStudent(); 原来的写法
CStudent stu;
stu = GetStudent();
}
/***************************************************************************/

这样先定义再赋值则大为不同!!!
stu此时作为main函数的局部变量则会调用构造函数进行初始化。另外调用GetStudent()时和上面一样,因为返回的是对象,则需要一个对象的地址,这里并不是将stu的地址交给它,因为stu已经构造好了,不能交给别的函数随意处理。而是会产生一个临时的匿名对象!!!
这个临时的匿名对象将代替之前第一种写法的做法,将自己地址交给GetStudent(),完成自己
内存的赋值。最后,通过stu的赋值操作符operator=将临时对象的值交给stu自己。再然后,临时对象完成自己的使命,立即析构,而不会等到main函数退出时。当然这个匿名的临时对象同样作为main函数的局部变量,在main函数栈帧建立时就预留了空间,这是编译器在编译的时候发现了需要一个临时对象来完成任务所以为其预留了位置。

所以,由上看出,第二种写法将增加临时对象的开销,还有进行赋值操作的开销。

如果对象比较复杂,这个开销是不能忍受的。比如下面的类型,赋值和拷贝都需要进行堆内存的操作,消耗时间。
/***************************************************************************/
class Array
{
public:
Array(int l) //构造函数
{
pData = new int[l];
len = l;
} ~Array() //析构函数
{
delete pData;
len = ;
} Array(const Array& other) //拷贝构造函数
{
pData = new int[other.len];
memcpy(pData, other.pData, other.len);
len = other.len;
} Array& operator=(const Array& other) //赋值操作符
{
pData = new int[other.len];
memcpy(pData, other.pData, other.len);
len = other.len;
return *this;
} private:
int* pData;
int len;
};
/***************************************************************************/
这便是C++中臭名昭著的临时对象性能问题!
在C++11中为了解决这个问题,引入了右值引用和move语意!
C++98规定了左值引用,如
int a;
int &b = a;
这里,b作为一个引用,引用了变量a的值,这里a是一个左值。
C++11引入了右值引用:
int &&m = 5;
这里会将5转换为一个临时对象(int型变量),然后m引用这个临时对象。
既然是右值引用,只可以引用临时对象——即右值!

这样,在C++11里,类引入了转移构造函数和转移赋值操作符,如下:

/***************************************************************************/
class Array
{
public:
Array(int l);       //构造函数
~Array();      //析构函数
Array(const Array& other); //拷贝构造函数
Array& operator=(const Array& other); //赋值操作符 Array(Array&& other)      //转移构造函数
{
pData = other.pData;
len = other.len; /*将资源转移过来,避免资源拷贝*/
other.pData = NULL;
other.len = ;
} Array& operator=(Array&& other) //转移赋值操作符
{
pData = other.pData;
len = other.len; /*将资源转移过来,避免资源拷贝*/
other.pData = NULL;
other.len = ; return *this;
} private:
int* pData;
int len;
}; Arrar GetArray()
{
Array loc_num(); /*some operation*/ return loc_num;
} int main()
{
Array num();
num = GetArray();
}
/***************************************************************************/

这样,当main函数中调用GetArray()时,虽然会有一个临时匿名对象产生(前面说了,用于GetArray填充返回值用),但这里在将临时对象的内容赋值给局部对象num时,将自动识别为右值赋值,不会调用原始赋值函数Array& operator=(const Array& other);转而调用转移赋值函数Array& operator=(const Array&& other);不会进行内存二次分配,从而节省开销。

【编程篇】C++11系列之——临时对象分析的更多相关文章

  1. 11.C++-临时对象分析

    首先来参考以下代码: #include <stdio.h> class Test { int mi; public: Test(int i) { mi = i; } Test() { Te ...

  2. 3.1Python数据处理篇之Numpy系列(一)---ndarray对象的属性与numpy的数据类型

    目录 目录 (一)简单的数组创建 1.numpy的介绍: 2.numpy的数组对象ndarray: 3.np.array(list/tuple)创建数组: (二)ndarray对象的属性 1.五个常用 ...

  3. 4.7Python数据处理篇之Matplotlib系列(七)---matplotlib原理分析

    目录 目录 前言 (一)总框架分析 (二)函数式的绘图 1.说明: 2.函数绘图的缺优点 3.绘图类的函数 4.操作类的函数 5.例子: (三)面向对象式的绘图 1.基本概念 2.基本对象 3.面向对 ...

  4. 如何用ABP框架快速完成项目(面向项目交付编程面向客户编程篇)(1) - 目录

    昨天发表了<如何用ABP框架快速完成项目 - 自动化测试 - 前端angular e2e protractor>后,大家十分热情,几个小时内就收到了不少问题,包括: 对于ui自动化测试这方 ...

  5. PHP求职宝典系列——PHP Web 编程篇

    PHP Web 编程篇 form表单 1.简述 POST 和 GET 传输的最大容量分别是多少? GET 方法提交的表单数据被附加到 URL 上,并作为URL 的一部分发送到服务器端. URL 的长度 ...

  6. Java并发编程锁系列之ReentrantLock对象总结

    Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...

  7. JSP第四篇【EL表达式介绍、获取各类数据、11个内置对象、执行运算、回显数据、自定义函数、fn方法库】

    什么是EL表达式? 表达式语言(Expression Language,EL),EL表达式是用"${}"括起来的脚本,用来更方便的读取对象! EL表达式主要用来读取数据,进行内容的 ...

  8. day7_直播_网络编程篇(元昊老师著)

    网络编程篇计算机网络: 多台独立的计算机用网络通信设备连接起来的网络.实现资源共享和数据传递. 比如,我们之前的学过的知识可以将D盘的一个文件传到C盘,但如果你想从你的电脑传一个文件到我的电脑上目前是 ...

  9. SAP接口编程 之 JCo3.0系列(01):JCoDestination

    SAP接口编程 之 JCo3.0系列(01):JCoDestination 字数2101 阅读103 评论0 喜欢0 JCo3.0是Java语言与ABAP语言双向通讯的中间件.与之前1.0/2.0相比 ...

随机推荐

  1. ecshop 后台 审核功能

    有三个关键文件 html文件<img src="images/{if $vo.is_check}yes{else}no{/if}.gif" onclick="lis ...

  2. Solr学习总结(四)Solr查询参数

    今天还是不会涉及到.Net和数据库操作,主要还是总结Solr 的查询参数,还是那句话,只有先明白了solr的基础内容和查询语法,后续学习solr 的C#和数据库操作,都是水到渠成的事.这里先列出sol ...

  3. [IOS基础]关于IOS的UIScreeen,UIView,UIViewController,UIWindow理解

    UIScreen: 代表当前这个屏幕,通过UIApplication可以获得这个属性 UIView:   一个矩形试图,包含用户手势和时间响应 UIViewController: 一个UIView的集 ...

  4. eclipse version

    查看Eclipse的版本号: 1. 找到eclipse安装目录 2. 进入readme文件夹,打开readme_eclipse.html 3. readme_eclipse.html呈现的第二行即数字 ...

  5. linux下nat配置

    iptables要启用nat表,必须启动nat表的支持.默认情况下,linux下是没有开启nat表的支持的. #启动内核的路由功能 echo > /proc/sys/net/ipv4/ip_fo ...

  6. Java Class类及反射机制

    java.lang.Class类 声明: public final class Class<T>extends Object implements Serializable, Generi ...

  7. ubuntu下安装chrome

    首先.题主在试过直接ubuntu终端命令安装chrome失败. 把经历过的错误稍微提一下: 在终端输入 下载安装包 sudo wget https://dl.google.com/linux/dire ...

  8. 【安装Express】CentOS7 下安装NodeJs+Express+MongoDB+Redis

    上一篇介绍了一下怎么安装Nodejs,那么这一篇就说说怎么安装express,express有个中文站点非常非常方便,http://www.expressjs.com.cn/创建express框架的站 ...

  9. epoll示例

    书到用时方恨少,一切尽在不言中 #include <iostream> #include <sys/socket.h> #include <sys/epoll.h> ...

  10. ios 导航栏的显示和隐藏切换

    从简单的一个没有导航栏的界面A push到另一个有导航栏的界面 B,在界面A的逻辑中加入下面逻辑: 屏幕快照 2016-03-30 上午10.35.24.png 这样完美的处理了这个场景变换需求.引起 ...