http://www.cppblog.com/besterChen/category/9573.html

所属分类: C/C++/STL/boost 

在函数调用的时候,无论是参数为对象还是返回一个对象,都将产生一个临时对象。这个笔记就是为了学习这个临时对象的产生过程而写。

本代码的详细例子见实例代码Ex.01

Ok,先让我们定义一个类:

class CExample

{

public:

int m_nFirstNum;

int m_nSecNum;

int GetSum();

bool SetNum(int nFirst, int nSec);

CExample(){}                            // 空构造,不实现任何功能

virtual ~CExample(){}                 // 空析构

};

// 定义的函数实现部分

int CExample::GetSum()

{

return m_nFirstNum+m_nSecNum;

}

先让我们看一下对象的创建过程

CExample objExp1;

//    00401393   lea         ecx,[ebp-18h]                  //  第一个对象

//    00401396   call        @ILT+20(CExample::CExample)

//    0040139B   mov         dword ptr [ebp-4],0              用来统计当前对象个数

CExample objExp2;

//    004013A2   lea         ecx,[ebp-24h]                  //  第二个对象

//    004013A5   call        @ILT+20(CExample::CExample)

//    004013AA   mov         byte ptr [ebp-4],1

CExample objExp3;

//    004013AE   lea         ecx,[ebp-30h]                  //  第三个对象

//    004013B1   call        @ILT+20(CExample::CExample)

//    004013B6   mov         byte ptr [ebp-4],2

上面创建了三个对象,它们的过程都非常相似,将一个局部变量地址给了ECX寄存器,然后调用构造函数。

先让我们看看,构造函数都干啥了:

12:   CExample::CExample()

13:   {

00401540   push        ebp

00401541   mov         ebp,esp

00401543   sub         esp,44h

00401546   push        ebx

00401547   push        esi

00401548   push        edi

00401549   push        ecx                                            // 保存寄存器环境

0040154A   lea         edi,[ebp-44h]

0040154D   mov         ecx,11h

00401552   mov         eax,0CCCCCCCCh

00401557   rep stos    dword ptr [edi]

00401559   pop         ecx                                                       // 填充完CC以后,恢复ECX内容

0040155A   mov         dword ptr [ebp-4],ecx

0040155D   mov         eax,dword ptr [ebp-4]                           // 取到this指针

00401560   mov         dword ptr [eax],offset CExample::`vftable'   // 让this指针指向虚表

15:   }

00401566   mov         eax,dword ptr [ebp-4]

00401569   pop         edi

0040156A   pop         esi

0040156B   pop         ebx

0040156C   mov         esp,ebp

0040156E   pop         ebp

0040156F   ret

我们知道,我们再C代码中,实现的是空构造,没有添加任何功能,可是反汇编的时候,发现,函数应该有个参数(是this指针),定位虚表的时候,是又构造完成让this指向虚表的工作的。

1、  传递一个对象的过程:

bool SetExpFun(CExample objExp)

{

g_objExp.SetNum(objExp.m_nFirstNum, objExp.m_nSecNum);

return true;

}

这是我们样例程序中,一个对象作为参数的情况。我们编写如下的调用代码:

SetExpFun(objExp1);

反汇编代码如下:

004013C8   sub         esp,0Ch                                    //     申请临时对象空间

004013CB   mov        ecx,esp                                     //     让ECX指向临时申请的对象

004013CD   mov        dword ptr [ebp-34h],esp            //   赋值一份this

004013D0   lea         eax,[ebp-18h]                             //   获取第一个对象的this指针

004013D3   push        eax                                              //   传递参数

004013D4   call        @ILT+45(CExample::CExample)   //   使用了拷贝构造所以有上面的参数

004013D9   mov        dword ptr [ebp-48h],eax             //   产生一个临时对象并保存它的this指针

004013DC   call        @ILT+15(SetExpFun) (00401014)  //  调用函数

004013E1   add         esp,0Ch

上面代码中,有两处函数调用,一个是我们已经非常熟悉的调用构造函数,另一个事调用我们需要的setExpFun函数,当然,通过上面的注释,我们很容易就能知道,在这里创建了一个临时的对象,而且貌似调用构造函数的时候还传递了一个参数(参数是我们定义的第一个对象: objExp1)。

是的,很明显这里是个拷贝构造,让我们先来看下它的调用过程。

拷贝构造

{

004011F0   push         ebp

004011F1   mov         ebp,esp

004011F3   sub          esp,44h

004011F6   push         ebx

004011F7   push         esi

004011F8   push         edi

004011F9   push         ecx                              ; 保存临时对象的this指针

004011FA   lea          edi,[ebp-44h]

004011FD   mov         ecx,11h

00401202   mov         eax,0CCCCCCCCh

00401207   rep stos       dword ptr [edi]

00401209   pop          ecx                              ; 找到调用时传递的临时对象的this指针

0040120A   mov         dword ptr [ebp-4],ecx

0040120D   mov         eax,dword ptr [ebp-4]

00401210   mov               ecx,dword ptr [ebp+8]   ; 参数对象的this指针,ECX中是虚表

00401213   mov         edx,dword ptr [ecx+4]   ; 取出参数对象的第一个成员

00401216   mov         dword ptr [eax+4],edx    ; 并赋值给临时对象的第一个成员

00401219   mov         eax,dword ptr [ebp-4]

0040121C   mov         ecx,dword ptr [ebp+8]

0040121F   mov         edx,dword ptr [ecx+8]   ; 取到参数对象的第二个成员

00401222   mov         dword ptr [eax+8],edx    ; 并赋值给临时对象的第二个成员

00401225   mov         eax,dword ptr [ebp-4]    ; 设置临时对象的虚表

00401228   mov         dword ptr [eax],offset CExample::`vftable'

0040122E   mov         eax,dword ptr [ebp-4]    ; 返回一个临时对象

00401231   pop          edi

00401232   pop          esi

00401233   pop          ebx

00401234   mov         esp,ebp

00401236   pop          ebp

00401237   ret           4

}

从上面的代码不难看出,我们这个拷贝构造直接在参数中改写的数据,等出来这个函数,我们main函数中:

004013C8   sub         esp,0Ch

申请的临时对象空间中就是一个完整的对象了。

好现在我们继续跟踪调用传参的代码:

16:   bool SetExpFun(CExample objExp)

17:   {

004012C0   push        ebp

004012C1   mov        ebp,esp

004012C3   push        0FFh

004012C5   push        offset __ehhandler$?SetExpFun@@YA_NVCExample@@@Z

004012CA   mov        eax,fs:[00000000]

004012D0   push        eax

004012D1   mov        dword ptr fs:[0],esp

004012D8   sub         esp,44h

004012DB   push        ebx

004012DC   push        esi

004012DD   push        edi

004012DE   lea          edi,[ebp-50h]

004012E1   mov         ecx,11h

004012E6   mov         eax,0CCCCCCCCh

004012EB   rep stos      dword ptr [edi]

004012ED   mov        dword ptr [ebp-4],0                      ; 计数对象数量

18:       g_objExp.SetNum(objExp.m_nFirstNum, objExp.m_nSecNum);

004012F4   mov         eax,dword ptr [ebp+0Ch]                    ; 直接引用临时对象的成员

004012F7   push         eax

004012F8   mov         ecx,dword ptr [ebp+10h]

004012FB   push         ecx

004012FC   mov         ecx,offset g_objExp                     ; 传递this指针

00401301   call          @ILT+0(CExample::SetNum)

19:       return true;

00401306   mov         byte ptr [ebp-10h],1

0040130A   mov         dword ptr [ebp-4],0FFFFFFFFh   ; 清空临时对象计数

00401311   lea          ecx,[ebp+8]                                  ; 取到临时对象的this指针

00401314   call         @ILT+40(CExample::~CExample)

00401319   mov         al,byte ptr [ebp-10h]

20:   }

0040131C   mov         ecx,dword ptr [ebp-0Ch]

0040131F   mov         dword ptr fs:[0],ecx

00401326   pop         edi

00401327   pop         esi

00401328   pop         ebx

00401329   add         esp,50h

0040132C   cmp         ebp,esp

0040132E   call        __chkesp (00401610)

00401333   mov         esp,ebp

00401335   pop         ebp

00401336   ret

2、  返回一个对象的过程:

CExample GetExpFun()

{

return g_objExp;

}

编写如下的调用代码:

// 下面是返回对象的情况

objExp2 = GetExpFun();

调试下这个程序:

59:       objExp2 = GetExpFun();

004013E4   lea         ecx,[ebp-40h]           ; 返回的临时对象空间是进入main函数的时候,提前分配好的。

004013E7   push        ecx                                ;             先将对象压栈

004013E8   call        @ILT+25(GetExpFun)      ;             调用函数

11:   CExample GetExpFun()

12:   {

00401190   push        ebp

00401191   mov         ebp,esp

00401193   sub         esp,44h

00401196   push        ebx

00401197   push        esi

00401198   push        edi

00401199   lea         edi,[ebp-44h]

0040119C   mov         ecx,11h

004011A1   mov         eax,0CCCCCCCCh

004011A6   rep stos    dword ptr [edi]

004011A8   mov         dword ptr [ebp-4],0

13:       return g_objExp;

004011AF   push        offset g_objExp (0042af80)

004011B4   mov         ecx,dword ptr [ebp+8]                 ; 引用传进来的参数对象指针

004011B7   call        @ILT+45(CExample::CExample)       ; 调用构造创建对象

004011BC   mov         eax,dword ptr [ebp-4]

004011BF   or          al,1

004011C1   mov         dword ptr [ebp-4],eax                  ; 更新对象个数

004011C4   mov         eax,dword ptr [ebp+8]                 ; 返回……

14:   }

004011C7   pop         edi

004011C8   pop         esi

004011C9   pop         ebx

004011CA   add         esp,44h

004011CD   cmp         ebp,esp

004011CF   call        __chkesp

004011D4   mov         esp,ebp

004011D6   pop         ebp

004011D7   ret

004013ED   add         esp,4

004013F0   mov         dword ptr [ebp-4Ch],eax                     ; 保存临时对象的指针

004013F3   mov         edx,dword ptr [ebp-4Ch]

004013F6   mov         dword ptr [ebp-50h],edx

004013F9   mov         byte ptr [ebp-4],3

004013FD   mov         eax,dword ptr [ebp-50h]              ; 这里重载的 = 运算符,因此将副本压栈做复制操作

00401400   push        eax

00401401   lea         ecx,[ebp-24h]                                ; 得到第二个对象的this指针

00401404   call        @ILT+10(CExample::operator=)

00401409   mov         byte ptr [ebp-4],2

0040140D   lea         ecx,[ebp-40h]                                ; 使用完成,释放临时对象

00401410   call        @ILT+40(CExample::~CExample)

printf("%d\r\n", objExp2.GetSum());

OK,只要捣鼓明白了这个临时对象,那我们的好多问题都可以解决了。

C++中临时对象的学习笔记的更多相关文章

  1. JavaSE中Collection集合框架学习笔记(3)——遍历对象的Iterator和收集对象后的排序

    前言:暑期应该开始了,因为小区对面的小学这两天早上都没有像以往那样一到七八点钟就人声喧闹.车水马龙. 前两篇文章介绍了Collection框架的主要接口和常用类,例如List.Set.Queue,和A ...

  2. JavaSE中Collection集合框架学习笔记(2)——拒绝重复内容的Set和支持队列操作的Queue

    前言:俗话说“金三银四铜五”,不知道我要在这段时间找工作会不会很艰难.不管了,工作三年之后就当给自己放个暑假. 面试当中Collection(集合)是基础重点.我在网上看了几篇讲Collection的 ...

  3. 浏览器中js执行机制学习笔记

    浏览器中js执行机制学习笔记 RiverSouthMan关注 0.0772019.05.15 20:56:37字数 872阅读 291 同步任务 当一个脚本第一次执行的时候,js引擎会解析这段代码,并 ...

  4. JavaSE中Collection集合框架学习笔记(1)——具有索引的List

    前言:因为最近要重新找工作,Collection(集合)是面试中出现频率非常高的基础考察点,所以好好恶补了一番. 复习过程中深感之前的学习不系统,而且不能再像刚毕业那样死背面试题,例如:String是 ...

  5. JS和JQuery中的事件托付 学习笔记

    事件托付事实上并非一个非常高级的技巧,比方在一个页面里面.当仅仅存在两个button的时候.可能你给button加入监听是这种:(本文不考虑浏览器兼容性.关于事件的兼容性可參考前面的学习笔记) < ...

  6. <<C++标准程序库>>中的STL简单学习笔记

    0. 内容为个人学习笔记, 仅供参考, 如有错漏, 欢迎指正! 1. STL中的所有组件都是由模板构成的, 所以其元素可以是任意型别的. 组件有: - 容器: 管理某类对象的集合. 不同的容器有各自的 ...

  7. [转] C++中临时对象及返回值优化

    http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...

  8. DFS中的奇偶剪枝学习笔记

    奇偶剪枝学习笔记 描述 编辑 现假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点, s | | | + — — — e 如图所示(“|”竖走,“—”横走,“+”转弯),易证abs( ...

  9. 转:C++中临时对象及返回值优化

    http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...

随机推荐

  1. BITED数学建模七日谈之五:怎样问数学模型问题

    下面进入数学建模经验谈第五天:怎样问数学模型问题 写这一篇的目的主要在于帮助大家能更快地发现问题和解决问题,让自己的模型思路有一个比较好的形成过程. 在我们学习数学模型.准备比赛的时候,经常会遇到各种 ...

  2. Java正则表达式获取网页所有网址和链接文字

    ;         pos1= urlContent.indexOf(strAreaBegin)+strAreaBegin.length();         pos2=urlContent.inde ...

  3. linux3.0.4编译LDD中的scull全过程

    按照惯例,我是应该先写一些本章的收获的,不过太晚了. 在看完第三章之后开始编译,错误一堆,几乎崩溃,幸亏经过不断的百度,总算解决了问题,我发现 我遇到问题比较多,算是集中七个龙珠了吧,感谢先行的大神们 ...

  4. 王家林的“云计算分布式大数据Hadoop实战高手之路---从零开始”的第十一讲Hadoop图文训练课程:MapReduce的原理机制和流程图剖析

    这一讲我们主要剖析MapReduce的原理机制和流程. “云计算分布式大数据Hadoop实战高手之路”之完整发布目录 云计算分布式大数据实战技术Hadoop交流群:312494188,每天都会在群中发 ...

  5. shell脚本操作mysql库

    shell脚本操作mysql数据库-e参数执行各种sql(指定到处编码--default-character-set=utf8 -s,去掉第一行的字段名称信息-N) 2011-05-11 18:18: ...

  6. Spark RDD概念学习系列之RDD的checkpoint(九)

     RDD的检查点 首先,要清楚.为什么spark要引入检查点机制?引入RDD的检查点?  答:如果缓存丢失了,则需要重新计算.如果计算特别复杂或者计算耗时特别多,那么缓存丢失对于整个Job的影响是不容 ...

  7. G450 Ubuntu14 无线网卡解决

    安装了Ubuntu14,与win7共存. grub界面启动. G450的本子,安装完之后发现无线网卡不能被驱动,但能被之别到,因此激活一次broadcom sta wireless driver 命令 ...

  8. 删除对象中的key

    delete obj.a; delete obj["a"];

  9. 关于JPA封装数据库数据到实体不调用属性的get和set的方法解决办法

    今天发现JPA封装数据库数据到实体并不调用属性的get和set的,郁闷,本来想在set方法做改字段的值处理的谁知道遇到这个情况: @Column(name = acode) @Access(value ...

  10. jquery ajax 序列化表单传参提交实体对象到后台action

    ========action后台我这里使用的是SpringMVC如果用ssh用法一致=============== @Controller@RequestMapping("PubjobCon ...