C++中临时对象的学习笔记
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++中临时对象的学习笔记的更多相关文章
- JavaSE中Collection集合框架学习笔记(3)——遍历对象的Iterator和收集对象后的排序
前言:暑期应该开始了,因为小区对面的小学这两天早上都没有像以往那样一到七八点钟就人声喧闹.车水马龙. 前两篇文章介绍了Collection框架的主要接口和常用类,例如List.Set.Queue,和A ...
- JavaSE中Collection集合框架学习笔记(2)——拒绝重复内容的Set和支持队列操作的Queue
前言:俗话说“金三银四铜五”,不知道我要在这段时间找工作会不会很艰难.不管了,工作三年之后就当给自己放个暑假. 面试当中Collection(集合)是基础重点.我在网上看了几篇讲Collection的 ...
- 浏览器中js执行机制学习笔记
浏览器中js执行机制学习笔记 RiverSouthMan关注 0.0772019.05.15 20:56:37字数 872阅读 291 同步任务 当一个脚本第一次执行的时候,js引擎会解析这段代码,并 ...
- JavaSE中Collection集合框架学习笔记(1)——具有索引的List
前言:因为最近要重新找工作,Collection(集合)是面试中出现频率非常高的基础考察点,所以好好恶补了一番. 复习过程中深感之前的学习不系统,而且不能再像刚毕业那样死背面试题,例如:String是 ...
- JS和JQuery中的事件托付 学习笔记
事件托付事实上并非一个非常高级的技巧,比方在一个页面里面.当仅仅存在两个button的时候.可能你给button加入监听是这种:(本文不考虑浏览器兼容性.关于事件的兼容性可參考前面的学习笔记) < ...
- <<C++标准程序库>>中的STL简单学习笔记
0. 内容为个人学习笔记, 仅供参考, 如有错漏, 欢迎指正! 1. STL中的所有组件都是由模板构成的, 所以其元素可以是任意型别的. 组件有: - 容器: 管理某类对象的集合. 不同的容器有各自的 ...
- [转] C++中临时对象及返回值优化
http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...
- DFS中的奇偶剪枝学习笔记
奇偶剪枝学习笔记 描述 编辑 现假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点, s | | | + — — — e 如图所示(“|”竖走,“—”横走,“+”转弯),易证abs( ...
- 转:C++中临时对象及返回值优化
http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...
随机推荐
- Android 横屏时禁止输入法全屏
在自己EditText的xml里加上属性 android:imeOptions="flagNoExtractUi"
- Hadoop开发环境简介(转)
1.Hadoop开发环境简介 1.1 Hadoop集群简介 Java版本:jdk-6u31-linux-i586.bin Linux系统:CentOS6.0 Hadoop版本:hadoop-1.0.0 ...
- CSRF的攻击与防御(转)
add by zhj:CSRF之所有发生,是因为http请求中会自动带上cookies,我的解决办法是:前端不要将数据放在cookie中,而是放在其它本地存储 (HTML5中称之为Web Storag ...
- cocos2d-x 3.2 例子文件工程的位置
更新到3.2后突然想要看看官方的例子,忽然发现在test中的cpp工程下面没有了工程的启动配置.奇怪,难道只有代码吗?重新查找后原来启动的工程文件都移动到了build文件夹下面,具体的路径就是 coc ...
- [转]比较 Rational Unified Process (RUP) 和 Microsoft Solutions Framework (MSF)
文档选项 将此页作为电子邮件发送 级别: 初级 Sandra Sergi Santos, 软件工程专家, IBM 2007 年 6 月 15 日 本文来自于 Rational Edge:Micro ...
- invoking gdb
[invoking gdb] The most usual way to start gdb is with one argument, specifying an executable progra ...
- java应用maven插件动态生成webservice代码
pom.xml如下 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www ...
- [原创]mac终端前面的计算机名怎么改??
1.修改-之前的名称 mac环境,系统 OS X Yisemite,打开终端, 执行下面命令“Tmp”是你想要改的电脑名称 sudo scutil --set HostName Tmp 执行前,执行后 ...
- oracle学习 十一 包+复合类型+自定义异常(持续更新)
在这里讲一下包的概念, 二话不说上个例子 包头: create or replace package pck_test is procedure proc_report_salary(name nva ...
- POJ1338Ugly Numbers(DP)
http://poj.org/problem?id=1338 第一反应就是DP,DP[i] = min{2*DP[j], 3*DP[k], 5*DP[p] j,k,p<i};于是枚举一下0-i- ...