c++虚继承汇编及内存布局分析(基于visual studio 2019)(三)
同时存在虚继承和虚函数
namespace test3 // 加入虚函数 { class Grand1 { public: virtual int G1Show(int param) { int res = param; return res; } int g1; }; class Base1 : virtual public Grand1 { public: int b1; }; class Base2 : virtual public Grand1 { public: int b2; }; class Derive : public Base1, public Base2 { public: int d; }; int main() { Derive d1; std::cout << sizeof(Base1) << " " << sizeof(Base2) << " " << sizeof(Derive) << std::endl; // 16(2个int+1个虚基类表指针+1个虚函数表指针) 16(2个int+1个虚基类表指针+1个虚函数表指针) 28(4个int+1个虚基类表指针+1个虚函数表指针) return 0; } /* class test3::Derive size(28): +--- 0 | +--- (base class test3::Base1) 0 | | {vbptr} 4 | | b1 | +--- 8 | +--- (base class test3::Base2) 8 | | {vbptr} 12 | | b2 | +--- 16 | d +--- +--- (virtual base test3::Grand1) 20 | {vfptr} 虚函数表所在位置 24 | g1 +--- test3::Derive::$vbtable@Base1@: 0 | 0 1 | 20 (Derived(Base1+0)Grand1) test3::Derive::$vbtable@Base2@: 0 | 0 1 | 12 (Derived(Base2+0)Grand1) test3::Derive::$vftable@: | -20 0 | &test3::Grand1::G1Show vbi: class offset o.vbptr o.vbte fVtorDisp test3::Grand1 20 0 4 0 */ } int main() { test3::main(); }
汇编代码:
Derive d1; d1的地址 00AFFB9C 00F82C1F push 1 00F82C21 lea ecx,[d1] 00F82C24 call test3::Derive::Derive (0F8132Fh) 进入构造函数的汇编代码进行分析
Project4.exe!test3::Derive::Derive(void): 00F821F0 push ebp 00F821F1 mov ebp,esp 00F821F3 sub esp,0CCh 00F821F9 push ebx 00F821FA push esi 00F821FB push edi 00F821FC push ecx 00F821FD lea edi,[ebp-0Ch] 00F82200 mov ecx,3 00F82205 mov eax,0CCCCCCCCh 00F8220A rep stos dword ptr es:[edi] 00F8220C pop ecx 00F8220D mov dword ptr [this],ecx 00F82210 cmp dword ptr [ebp+8],0 00F82214 je __$EncStackInitStart+37h (0F82234h) 此处没有跳转,上一句[ebp+8]不等于0 00F82216 mov eax,dword ptr [this] 00F82219 mov dword ptr [eax],offset test3::Derive::`vbtable' (0F89BB8h) 虚基类表1指针进行保存到对象内起始位置 00F8221F mov eax,dword ptr [this] 00F82222 mov dword ptr [eax+8],offset test3::Derive::`vbtable' (0F89BC4h) 虚基类表2指针进行保存到对象内起始位置后8个字节处 00F82229 mov ecx,dword ptr [this] 00F8222C add ecx,14h 00F8222F call test3::Grand1::Grand1 (0F81460h) 跳转到虚基类Grand1的构造函数进行分析 00F82234 push 0 00F82236 mov ecx,dword ptr [this] 00F82239 call test3::Base1::Base1 (0F811EAh) 跳转到Base1构造函数进行分析 00F8223E push 0 00F82240 mov ecx,dword ptr [this] 00F82243 add ecx,8 00F82246 call test3::Base2::Base2 (0F81280h) Base2构造函数类似Base1 00F8224B mov eax,dword ptr [this] 00F8224E mov ecx,dword ptr [eax] 00F82250 mov edx,dword ptr [ecx+4] 00F82253 mov eax,dword ptr [this] 00F82256 mov dword ptr [eax+edx],offset test3::Derive::`vftable' (0F89BB0h) 虚函数表 00F8225D mov eax,dword ptr [this] 00F82260 pop edi 00F82261 pop esi 00F82262 pop ebx 00F82263 add esp,0CCh 00F82269 cmp ebp,esp 00F8226B call __RTC_CheckEsp (0F812BCh) 00F82270 mov esp,ebp 00F82272 pop ebp 00F82273 ret 4 00F82276 int 3 00F82277 int 3 00F82278 int 3 00F82279 int 3
Project4.exe!test3::Grand1::Grand1(void): 00F822A0 push ebp 00F822A1 mov ebp,esp 00F822A3 sub esp,0CCh 00F822A9 push ebx 00F822AA push esi 00F822AB push edi 00F822AC mov dword ptr [this],ecx 00F822AF mov eax,dword ptr [this] 00F822B2 mov dword ptr [eax],offset test3::Grand1::`vftable' (0F89B74h) 虚函数表赋值 00F822B8 mov eax,dword ptr [this] 00F822BB pop edi 00F822BC pop esi 00F822BD pop ebx 00F822BE mov esp,ebp 00F822C0 pop ebp 00F822C1 ret 00F822C2 int 3 00F822C3 int 3
Project4.exe!test3::Base1::Base1(void): 00F81FE0 push ebp 00F81FE1 mov ebp,esp 00F81FE3 sub esp,0CCh 00F81FE9 push ebx 00F81FEA push esi 00F81FEB push edi 00F81FEC push ecx 00F81FED lea edi,[ebp-0Ch] 00F81FF0 mov ecx,3 00F81FF5 mov eax,0CCCCCCCCh 00F81FFA rep stos dword ptr es:[edi] 00F81FFC pop ecx 00F81FFD mov dword ptr [this],ecx 00F82000 cmp dword ptr [ebp+8],0 00F82004 je __$EncStackInitStart+2Dh (0F8201Ah) je跳转指令,[ebp+8]==0,会跳转到00F8201A,黄色部分不会执行,已经在Derive的构造函数中执行过了 00F82006 mov eax,dword ptr [this] 00F82009 mov dword ptr [eax],offset test3::Base1::`vbtable' (0F89B88h) 00F8200F mov ecx,dword ptr [this] 00F82012 add ecx,8 00F82015 call test3::Grand1::Grand1 (0F81460h) 00F8201A mov eax,dword ptr [this] 00F8201D mov ecx,dword ptr [eax] 00F8201F mov edx,dword ptr [ecx+4] 00F82022 mov eax,dword ptr [this] 00F82025 mov dword ptr [eax+edx],offset test3::Base1::`vftable' (0F89B80h) 00F8202C mov eax,dword ptr [this] 00F8202F pop edi 00F82030 pop esi 00F82031 pop ebx 00F82032 add esp,0CCh 00F82038 cmp ebp,esp 00F8203A call __RTC_CheckEsp (0F812BCh) 00F8203F mov esp,ebp 00F82041 pop ebp 00F82042 ret 4 00F82045 int 3 00F82046 int 3 00F82047 int 3
Derive d1对象起始地址为00affb9c, 00f89bb8为虚基类表1, 00f89bc4为虚基类表2, 00f86bb0为虚函数表