所以,在书上,我们没有看到C++语义所说的,在new之后对类的构造函数的调用:如果有默认的则调用默认的,如果有自定义的,则自动调用用户自定义的构造函数。
所以在我采用默认的选项重新编译了一个。
下面贴出我得到的结果(平台VS2008,默认选项,命令行下编译:
示例代码:
#include
class Base{
public:
virtual void demo(){
printf("BASE\n");
}
virtual void demo_2(){
printf("BASE DEMO 2\n");
}
void demo_3(){
printf("Nonvirtual BASE DEMO 3\n");
}
};
class Derived:public Base{
public:
virtual void demo(){
printf("DERIVED\n");
}
virtual void demo_2(){
printf("DERIVED DEMO 2\n");
}
void demo_3(){
printf("Nonvirtual DERIVED DEMO 3\n");
}
};
int main(){
Base *p=new Base;
p->demo();
p->demo_2();
p->demo_3();
p=new Derived;
p->demo();
p->demo_2();
p->demo_3();
}
用IDA 5.2反汇编得到的结果:
.text:00401000 sub_401000 proc near ; CODE XREF: start-5Cp
.text:00401000
.text:00401000 var_14 = dword ptr -14h
.text:00401000 var_10 = dword ptr -10h
.text:00401000 var_C = dword ptr -0Ch
.text:00401000 var_8 = dword ptr -8
.text:00401000 var_4 = dword ptr -4
.text:00401000
.text:00401000 push ebp
.text:00401001 mov ebp, esp
.text:00401003 sub esp, 14h
.text:00401006 push 4
.text:00401008 call new ; 调用new操作分配内存
.text:0040100D add esp, 4
.text:00401010 mov [ebp+var_8], eax ; 保存分配到的指针到ebp+var_8
.text:00401013 cmp [ebp+var_8], 0
.text:00401017 jz short loc_401026
.text:00401019 mov ecx, [ebp+var_8] ; 利用ECX来传递this指针
.text:0040101C call Base__Constructor ; 调用Base的默认构造函数
.text:00401021 mov [ebp+var_10], eax ; 保存初始化后的实例指针到ebp+var_10
.text:00401024 jmp short loc_40102D
.text:00401026 ; ---------------------------------------------------------------------------
.text:00401026
.text:00401026 loc_401026: ; CODE XREF: sub_401000+17j
.text:00401026 mov [ebp+var_10], 0
.text:0040102D
.text:0040102D loc_40102D: ; CODE XREF: sub_401000+24j
.text:0040102D mov eax, [ebp+var_10]
.text:00401030 mov [ebp+var_4], eax
.text:00401033 mov ecx, [ebp+var_4]
.text:00401036 mov edx, [ecx]
.text:00401038 mov ecx, [ebp+var_4] ; this指针
.text:0040103B mov eax, [edx] ; 获得实例的第一个虚函数地址到EAX
.text:0040103D call eax ; virtual void Base::demo()
.text:0040103F mov ecx, [ebp+var_4]
.text:00401042 mov edx, [ecx]
.text:00401044 mov ecx, [ebp+var_4]
.text:00401047 mov eax, [edx+4] ; 移动Vtbl指针,保存地址到EAX
.text:0040104A call eax ; virtual void Base::demo_2()
.text:0040104C mov ecx, [ebp+var_4]
.text:0040104F call sub_4010B0 ; void Base::demo_3()
.text:0040104F ; 对于非虚函数,直接调用
.text:00401054 push 4
.text:00401056 call new
.text:0040105B add esp, 4
.text:0040105E mov [ebp+var_C], eax
.text:00401061 cmp [ebp+var_C], 0
.text:00401065 jz short loc_401074
.text:00401067 mov ecx, [ebp+var_C]
.text:0040106A call Derived__Constructor ; 调用Derived的默认构造函数
.text:0040106F mov [ebp+var_14], eax ; 保存初始化后的实例指针到ebp+var_14
.text:00401072 jmp short loc_40107B
.text:00401074 ; ---------------------------------------------------------------------------
.text:00401074
.text:00401074 loc_401074: ; CODE XREF: sub_401000+65j
.text:00401074 mov [ebp+var_14], 0
.text:0040107B
.text:0040107B loc_40107B: ; CODE XREF: sub_401000+72j
.text:0040107B mov ecx, [ebp+var_14]
.text:0040107E mov [ebp+var_4], ecx
.text:00401081 mov edx, [ebp+var_4]
.text:00401084 mov eax, [edx]
.text:00401086 mov ecx, [ebp+var_4]
.text:00401089 mov edx, [eax]
.text:0040108B call edx
.text:0040108D mov eax, [ebp+var_4]
.text:00401090 mov edx, [eax]
.text:00401092 mov ecx, [ebp+var_4]
.text:00401095 mov eax, [edx+4]
.text:00401098 call eax
.text:0040109A mov ecx, [ebp+var_4]
.text:0040109D call sub_4010B0
.text:004010A2 xor eax, eax
.text:004010A4 mov esp, ebp
.text:004010A6 pop ebp
.text:004010A7 retn
.text:004010A7 sub_401000 end
类Base的默认构造函数:
.text:004010D0 Base__Constructor proc near ; CODE XREF: sub_401000+1Cp
.text:004010D0 ; Derived__Constructor+Ap
.text:004010D0
.text:004010D0 var_4 = dword ptr -4
.text:004010D0
.text:004010D0 push ebp
.text:004010D1 mov ebp, esp
.text:004010D3 push ecx
.text:004010D4 mov [ebp+var_4], ecx
.text:004010D7 mov eax, [ebp+var_4]
.text:004010DA mov dword ptr [eax], offset off_40A16C
.text:004010E0 mov eax, [ebp+var_4]
.text:004010E3 mov esp, ebp
.text:004010E5 pop ebp
.text:004010E6 retn
.text:004010E6 Base__Constructor endp
类Derived的默认构造函数与Base的类似,但是多了一个对Base的默认构造函数的调用:
.text:00401130 Derived__Constructor proc near ; CODE XREF: sub_401000+6Ap
.text:00401130
.text:00401130 var_4 = dword ptr -4
.text:00401130
.text:00401130 push ebp
.text:00401131 mov ebp, esp
.text:00401133 push ecx
.text:00401134 mov [ebp+var_4], ecx
.text:00401137 mov ecx, [ebp+var_4]
.text:0040113A call Base__Constructor
.text:0040113F mov eax, [ebp+var_4]
.text:00401142 mov dword ptr [eax], offset off_40A190
.text:00401148 mov eax, [ebp+var_4]
.text:0040114B mov esp, ebp
.text:0040114D pop ebp
.text:0040114E retn
.text:0040114E Derived__Constructor endp
另外,我们也看一下类的结构:
class Base size(4):
+---
0 | {vfptr}
+---
Base::$vftable@:
| &Base_meta
| 0
0 | &Base::demo
1 | &Base::demo_2
Base::demo this adjustor: 0
Base::demo_2 this adjustor: 0
class Derived size(4):
+---
| +--- (base class Base)
0 | | {vfptr}
| +---
+---
Derived::$vftable@:
| &Derived_meta
| 0
0 | &Derived::demo
1 | &Derived::demo_2
Derived::demo this adjustor: 0
Derived::demo_2 this adjustor: 0
采用优化选项"/O2"后,反汇编得到的结果,这里也一并帖出:
.text:00401040 sub_401040 proc near ; CODE XREF: start-5Cp
.text:00401040 push esi
.text:00401041 push 4
.text:00401043 call new
.text:00401048 add esp, 4
.text:0040104B test eax, eax
.text:0040104D jz short loc_401059
.text:0040104F mov dword ptr [eax], offset off_40A16C
.text:00401055 mov esi, eax
.text:00401057 jmp short loc_40105B
.text:00401059 ; ---------------------------------------------------------------------------
.text:00401059
.text:00401059 loc_401059: ; CODE XREF: sub_401040+Dj
.text:00401059 xor esi, esi
.text:0040105B
.text:0040105B loc_40105B: ; CODE XREF: sub_401040+17j
.text:0040105B mov eax, [esi]
.text:0040105D mov edx, [eax]
.text:0040105F mov ecx, esi
.text:00401061 call edx
.text:00401063 mov eax, [esi]
.text:00401065 mov edx, [eax+4]
.text:00401068 mov ecx, esi
.text:0040106A call edx
.text:0040106C push offset aNonvirtualBase ; "Nonvirtual BASE DEMO 3\n"
.text:00401071 call sub_4010B2
.text:00401076 push 4
.text:00401078 call new
.text:0040107D add esp, 8
.text:00401080 test eax, eax
.text:00401082 jz short loc_40108E
.text:00401084 mov dword ptr [eax], offset off_40A190
.text:0040108A mov esi, eax
.text:0040108C jmp short loc_401090
.text:0040108E ; ---------------------------------------------------------------------------
.text:0040108E
.text:0040108E loc_40108E: ; CODE XREF: sub_401040+42j
.text:0040108E xor esi, esi
.text:00401090
.text:00401090 loc_401090: ; CODE XREF: sub_401040+4Cj
.text:00401090 mov eax, [esi]
.text:00401092 mov edx, [eax]
.text:00401094 mov ecx, esi
.text:00401096 call edx
.text:00401098 mov eax, [esi]
.text:0040109A mov edx, [eax+4]
.text:0040109D mov ecx, esi
.text:0040109F call edx
.text:004010A1 push offset aNonvirtualBase ; "Nonvirtual BASE DEMO 3\n"
.text:004010A6 call sub_4010B2
.text:004010AB add esp, 4
.text:004010AE xor eax, eax
.text:004010B0 pop esi
.text:004010B1 retn
.text:004010B1 sub_401040 endp