c++虚函数表汇编及内存布局分析(基于visual studio 2019)(五)多继承虚函数内存分布
原始代码
namespace test3 { class Base1 { public: virtual void e() { std::cout << "e" << std::endl; } virtual void f() { std::cout << "f" << std::endl; } virtual void g() { std::cout << "g" << std::endl; } int b1; }; class Base2 { public: virtual void h() { std::cout << "h" << std::endl; } virtual void i() { std::cout << "i" << std::endl; } int b2; }; class Derive : public Base1, public Base2 // 多继承 { public: int d1; }; int main() { Derive d; int i = 100; Base2* b2 = &d; b2->h(); std::cout << sizeof(Base1) <<" " << sizeof(Base2)<<" " << sizeof(Derive) << std::endl; // 8(1个int+1个虚函数表指针) 8(同上) 20(8+8+1个int Derive中有2个虚函数表) return 0; } /* 虚函数表定义在虚函数出现的类里 class test3::Base1 size(8): +--- 0 | {vfptr} 4 | b1 +--- test3::Base1::$vftable@: | &Base1_meta | 0 0 | &test3::Base1::e 1 | &test3::Base1::f 2 | &test3::Base1::g test3::Base1::e this adjustor: 0 test3::Base1::f this adjustor: 0 test3::Base1::g this adjustor: 0 class test3::Base2 size(8): +--- 0 | {vfptr} 4 | b2 +--- test3::Base2::$vftable@: | &Base2_meta | 0 0 | &test3::Base2::h 1 | &test3::Base2::i test3::Base2::h this adjustor: 0 test3::Base2::i this adjustor: 0 Derive中包含的虚函数表指针分布在其继承的Base1和Base2内存布局里 class test3::Derive size(20): +--- 0 | +--- (base class test3::Base1) 0 | | {vfptr} 4 | | b1 | +--- 8 | +--- (base class test3::Base2) 8 | | {vfptr} 12 | | b2 | +--- 16 | d1 +--- test3::Derive::$vftable@Base1@: | &Derive_meta | 0 0 | &test3::Base1::e 1 | &test3::Base1::f 2 | &test3::Base1::g test3::Derive::$vftable@Base2@: | -8 应该表示虚函数表指针距离Derive起始位置的距离 0 | &test3::Base2::h 1 | &test3::Base2::i */ }
汇编代码分析
Derive d; 0020351F lea ecx,[d] 00203522 call test3::Derive::Derive (02013C0h) int i = 100; 00203527 mov dword ptr [i],64h Base2* b2 = &d; 0020352E lea eax,[d] 00203531 test eax,eax 00203533 je __$EncStackInitStart+47h (0203543h) 00203535 lea ecx,[d] 00203538 add ecx,8 0020353B mov dword ptr [ebp-0FCh],ecx 00203541 jmp __$EncStackInitStart+51h (020354Dh) 00203543 mov dword ptr [ebp-0FCh],0 0020354D mov edx,dword ptr [ebp-0FCh] 在栈中找到d对象中Base2部分的起始地址,然后赋值给b2 00203553 mov dword ptr [b2],edx b2->h(); 00203556 mov eax,dword ptr [b2] 拿到虚函数表地址 00203559 mov edx,dword ptr [eax] 0020355B mov esi,esp 0020355D mov ecx,dword ptr [b2] ecx中保存b2的起始位置,进入h函数后赋值给this指针 00203560 mov eax,dword ptr [edx] 准备调用虚函数表 00203562 call eax 00203564 cmp esi,esp 00203566 call __RTC_CheckEsp (020133Eh)