总所周知,构造函数是对象重要的组成部分,承担了对象的初始化工作。本文主要讲C++下对象的默认构造函数的反汇编代码,或许,这没什么用处,但是,知其然,还要知其所以然吧,了解底层,将对我们更好地掌握知识有很大帮助。打牢基础,将更有利于我们的成长。“勿在浮沙筑高台”-------侯捷。
当一个对象没有声明构造函数的时候,编译器会暗中为对象生成一个默认构造函数(Default Constructor),被暗中生成的的默认构造函数将是一个trivial(无用的,浅薄无能的)的函数。(摘自“深度探索C++对象模型 P40”)。下面我们来看看编译器生成的默认构造函数。
例子1:
class A
{
public:
void print(){};
};
int main()
{
A a;
a.print();
return 0;
};
在vs2008下main方法的汇编代码如下所示(按F5进入调试模式,之后“Alt+8”即可跳出代码和汇编的混合代码):
int main()
{
013A13A0 push ebp
013A13A1 mov ebp,esp
013A13A3 sub esp,0CCh
013A13A9 push ebx
013A13AA push esi
013A13AB push edi
013A13AC lea edi,[ebp-0CCh]
013A13B2 mov ecx,33h
013A13B7 mov eax,0CCCCCCCCh
013A13BC rep stos dword ptr es:[edi]
A a;
a.print();
013A13BE lea ecx,[a]
013A13C1 call A::print (13A100Fh)
return 0;
013A13C6 xor eax,eax
};
下面是这段汇编代码的解释,参考:http://johnlxj.diandian.com/?tag=edi:
push ebp;将ebp压入栈,ebp主要用来存储当前栈帧的栈底指针,所有的局部变量都是用相对于ebp的偏移来使用的。
move ebp,esp
sub esp,oCCh;这两句的意思是是将当前栈顶作为这一栈帧的基值。并且为本函数分配了0CCh*4个字节的栈空间。因为栈顶往上走了cc步。
push ebx
push esi
push edi;这三句的意思是将上个函数所使用的寄存器的值压入栈中,即保存上个方法所使用的寄存器值,为了执行完本函数后能返回去。
lea edi, [ebp-0CCh]
mov ecx,33h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]; 上面4句代码的意思是将ebp到0CCh的栈空间(33h * 4字节)通过rep(循环指令)置为0CCh。
通过上面的汇编,我们可以知道对于一个这样简单的类(只声明了一个方法的类),编译器生成的代码甚至没有调用它的构造函数(应该编译器没有暗中合成它的构造函数)。
例子2:
当我们将A的声明改为:
class A
{
public:
A(){};
void print(){};
};
反汇编的代码如下所示:
int main()
{
00B613B0 push ebp
00B613B1 mov ebp,esp
00B613B3 sub esp,0CCh
00B613B9 push ebx
00B613BA push esi
00B613BB push edi
00B613BC lea edi,[ebp-0CCh]
00B613C2 mov ecx,33h
00B613C7 mov eax,0CCCCCCCCh
00B613CC rep stos dword ptr es:[edi]
A a;
00B613CE lea ecx,[a]
00B613D1 call A::A (0B611D6h)
a.print();
00B613D6 lea ecx,[a]
00B613D9 call A::print (0B6100Fh)
return 0;
00B613DE xor eax,eax
};
A的构造函数反汇编如下:
A(){};
00B61440 push ebp
00B61441 mov ebp,esp
00B61443 sub esp,0CCh
00B61449 push ebx
00B6144A push esi
00B6144B push edi
00B6144C push ecx
00B6144D lea edi,[ebp-0CCh]
00B61453 mov ecx,33h
00B61458 mov eax,0CCCCCCCCh
00B6145D rep stos dword ptr es:[edi]
00B6145F pop ecx
00B61460 mov dword ptr [ebp-8],ecx
00B61463 mov eax,dword ptr [this]
00B61466 pop edi
00B61467 pop esi
00B61468 pop ebx
00B61469 mov esp,ebp
00B6146B pop ebp
00B6146C ret
对照前面的解释,A::A()是啥事都没做(在将申请的栈空间置为0后就开始将退出函数),这段汇编代码或许说明我们声明个空构造函数是件没意义的事(当然,带有成员初始化列表的空构造函数不算)。
例子3:
下面再给类的定义添些东西:
class A
{
public:
A(){};
void print(){};
private:
int m_nValue;
};
直接贴它的构造函数:
A(){};
00E21420 push ebp
00E21421 mov ebp,esp
00E21423 sub esp,0CCh
00E21429 push ebx
00E2142A push esi
00E2142B push edi
00E2142C push ecx
00E2142D lea edi,[ebp-0CCh]
00E21433 mov ecx,33h
00E21438 mov eax,0CCCCCCCCh
00E2143D rep stos dword ptr es:[edi]
00E2143F pop ecx
00E21440 mov dword ptr [ebp-8],ecx
00E21443 mov eax,dword ptr [this]
00E21446 pop edi
00E21447 pop esi
00E21448 pop ebx
00E21449 mov esp,ebp
00E2144B pop ebp
00E2144C ret
和例子2的构造函数对比,代码完全一样,这说明类中的成员变量在初始化的时候需要是一个特定值的话,应该自己手动初始化,编译器是不会负责这个事情的。但是,当我们类中有成员变量的时候,类构造的时候最好将成员变量初始化相应的初值,这是个良好的编程习惯,这次就先说到这里了,复杂的默认构造函数(成员变量有Default构造函数的默认构造函数,继承下的构造函数,有虚函数的构造函数)的反汇编会在下面的系列继续。
这是我第一次写博客,请大家多多指导,谢谢大家。
原文链接: https://www.cnblogs.com/yetuweiba/archive/2012/08/19/2646482.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/60081
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!