/*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. 关于application/x-www-form-urlencoded等字符编码的解释说明

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

  2. [UML]UML系列——用例图Use Case

    用例图的概念 用例图是描述用例.参与者以及它们之间关系的图. 用例图的作用 用例图是从用户的角度来描述对信息系统的需求,分析产品的功能和行为. 用例图定义和描述了系统的外部可见行为,是分析.设计直至组 ...

  3. 【06-23】js动画学习笔记01

    <html> <head> <style> * { margin:0; padding:0; } #div1{ width:200px; height:200px; ...

  4. 你想的到想不到的 javascript 应用小技巧方法

    javascript 在前端应用体验小技巧继续积累. 事件源对象 event.srcElement.tagName event.srcElement.type 捕获释放 event.srcElemen ...

  5. VBA笔记(一)

    开启VBA编程环境--VBE 方法一:按<Alt+F11>组合建 方法二:查看代码 宏设置 当然启用宏的设置方式不同,宏的启动方式也不一样. 首先打开"office 按钮&quo ...

  6. PHP 连接 MySQL

    PHP 连接 MySQL PHP 5 及以上版本建议使用以下方式连接 MySQL : MySQLi extension ("i" 意为 improved) PDO (PHP Dat ...

  7. javascript 函数与对象

    javascript中的函数是非常重要的概念,也是比较难于理解的一个知识点! 下面就来聊聊函数: JS基于对象:什么是基于对象呢?简单的说所有代码都是"对象"; 比如函数: fun ...

  8. SSAS动态添加分区(一)

    一.动态分区的好处就不说了,随着时间的推移,不可能一个度量值组都放在一个分区中,处理速度非常慢,如何动态添加分区,如何动态处理分区,成为了很多新手BI工程师一个头痛的问题,废话不多说,分享一下我的经验 ...

  9. APP常用字体

    font-family:Microsoft YaHei,Helvitica,Verdana,Tohoma,Arial,san-serif;

  10. 个性化设置phpMyAdmin,去掉“以树形显示数据库”,禁用“发送错误报告”

    个性化设置phpMyAdmin 在使用phpMyAdmin 3.5.8.2时,发现: 如果数据库有相同的前缀,左边数据库导航会把前缀合并,即所谓的“以树形显示数据库”,真的有点不习惯,如下图所示: 不 ...