虚函数
编译器会将该类中所有虚函数的首地址保存在一张地址表中,这张表被称为虚函数地址表,简称虚表。编译器还会在类的首部添加一个隐藏数据成员,称为虚表指针。保存着虚表的首地址
#include <stdio.h>
class Person { public: virtual int getAge() { return age; } virtual void setAge(int age) { this->age = age; } ~Person() { printf("~Person()\n"); } private: int age; };
int main(int argc, char* argv[]) { Person person; person.setAge(20); Person* ptr=&person; ptr->setAge(22); printf("%d\n", person.getAge()); return 0; }
|
.text:00000001400016E0 ; __unwind { // __CxxFrameHandler4_0 .text:00000001400016E0 mov [rsp+arg_8], rdx .text:00000001400016E5 mov [rsp+arg_0], ecx .text:00000001400016E9 sub rsp, 48h .text:00000001400016ED lea rcx, [rsp+48h+var_18] ; this .text:00000001400016F2 call j_??0Person@@QEAA@XZ ; Person::Person(void) .text:00000001400016F2 .text:00000001400016F7 nop .text:00000001400016F8 mov edx, 14h ; age .text:00000001400016FD lea rcx, [rsp+48h+var_18] ; this .text:0000000140001702 call j_?setAge@Person@@UEAAXH@Z ; Person::setAge(int) .text:0000000140001702 .text:0000000140001707 lea rax, [rsp+48h+var_18] .text:000000014000170C mov [rsp+48h+var_20], rax .text:0000000140001711 mov rax, [rsp+48h+var_20] ;rax为this .text:0000000140001716 mov rax, [rax] ;rax为虚表指针 .text:0000000140001719 mov edx, 16h ;参数:setAge(22) .text:000000014000171E mov rcx, [rsp+48h+var_20] ;this指针用来寻址age成员 .text:0000000140001723 call qword ptr [rax+8] ;利用虚表调用setAge .text:0000000140001723 .text:0000000140001726 lea rcx, [rsp+48h+var_18] ; this .text:000000014000172B call j_?getAge@Person@@UEAAHXZ ; Person::getAge(void) .text:000000014000172B .text:0000000140001730 mov edx, eax .text:0000000140001732 lea rcx, aD ; "%d\n" .text:0000000140001739 call j_printf .text:0000000140001739 .text:000000014000173E mov [rsp+48h+var_28], 0 .text:0000000140001746 lea rcx, [rsp+48h+var_18] ; this .text:000000014000174B call j_??1Person@@QEAA@XZ ; Person::~Person(void) .text:000000014000174B .text:0000000140001750 mov eax, [rsp+48h+var_28] .text:0000000140001754 add rsp, 48h .text:0000000140001758 retn
|
编译器自动添加构造函数,把类首部赋值为虚表指针
.rdata:0000000140007DE8 ??_7Person@@6B@ dq offset j_?getAge@Person@@UEAAHXZ, offset j_?setAge@Person@@UEAAXH@Z, 0
.text:0000000140001510 ; void __fastcall Person::Person(Person *this) .text:0000000140001510 ??0Person@@QEAA@XZ proc near ; CODE XREF: Person::Person(void)↑j .text:0000000140001510 .text:0000000140001510 arg_0= qword ptr 8 .text:0000000140001510 .text:0000000140001510 mov [rsp+arg_0], rcx .text:0000000140001515 mov rax, [rsp+arg_0] .text:000000014000151A lea rcx, ??_7Person@@6B@ ; const Person::`vftable' .text:0000000140001521 mov [rax], rcx .text:0000000140001524 mov rax, [rsp+arg_0] .text:0000000140001529 retn
|
析构函数会再次给类首部赋值虚表指针,让其指向自身的虚表首地址,
子类以后可能会被其他类继承成为父类,子类执行完析构函数执行父类的析构,此时this指针处虚表是子类的,如果不修改虚表就会执行到子类析构
.text:0000000140001570 ; void __fastcall Person::~Person(Person *this) .text:0000000140001570 ??1Person@@QEAA@XZ proc near ; CODE XREF: Person::~Person(void)↑j .text:0000000140001570 ; DATA XREF: .pdata:ExceptionDir↓o .text:0000000140001570 .text:0000000140001570 arg_0= qword ptr 8 .text:0000000140001570 .text:0000000140001570 mov [rsp+arg_0], rcx .text:0000000140001575 sub rsp, 28h .text:0000000140001579 mov rax, [rsp+28h+arg_0] .text:000000014000157E lea rcx, ??_7Person@@6B@ ; const Person::`vftable' .text:0000000140001585 mov [rax], rcx .text:0000000140001588 lea rcx, _Format ; "~Person()\n" .text:000000014000158F call j_printf .text:000000014000158F .text:0000000140001594 add rsp, 28h .text:0000000140001598 retn
|
同时可以发现直接使用对象调用自身虚函数时,被编译成直接call虚函数,不走虚表,效率高
通过指针或者引用(引用只是在编译期间多了检测的指针)调用虚函数是从虚表取出虚函数指针再调用
类与父类之间关系
在内存中的数据排列:先安排父类的数据,后安排子类新定义的数据。父类中声明为私有(private)的成员,子类对象无法直接访问,但是在子类对象的内存结构中,父类私有的成员数据依然存在。C++语法规定的访问控制仅限于编译层面,在编译的过程中由编译器进行语法检查
父类数据在前面方便了使用父类函数,直接传递子类this指针即可
对于子类无构造析构函数,父类有构造和析构函数,子类会提供默认构造和析构函数
构造:基类→基类的派生类→……→当前类。
析构:当前类→基类的派生类→……→基类。
#include <stdio.h>
class Person { public: Person() { showSpeak(); } virtual ~Person() { showSpeak(); } virtual void showSpeak() { printf("Speak Person\r\n"); } };
class Chinese : public Person { public: Chinese() {} virtual ~Chinese() {} virtual void showSpeak() { printf("Speak Chinese\r\n"); } };
class American : public Person { public: American() {} virtual ~American() {} virtual void showSpeak() { printf("Speak American\r\n"); } };
class German : public Person { public: German() {} virtual ~German() {} virtual void showSpeak() { printf("Speak German\r\n"); } };
void speak(Person* person) { person->showSpeak(); }
int main(int argc, char* argv[]) { Chinese chinese; American american; German german; speak(&chinese); speak(&american); speak(&german); return 0; }
|
子类会按照父类虚函数定义的顺序排列属于自己的虚表
调用构造或析构函数第一步都是将虚表指针换为该类的虚表指针,来调用到属于自己的函数
析构函数没有被定义为虚函数,那么编译器会按指针的类型调用父类的析构函数,从而引发错误
显式调用析构函数时不能马上释放堆内存,所以在析构函数的代理函数中通过一个参数控制是否释放内存,便于程序员管理析构函数的调用。
#include <stdio.h> #include <new.h>
class Person { public: Person() {} virtual ~Person() {} virtual void showSpeak() {} };
class Chinese : public Person { public: Chinese() {} virtual ~Chinese() {} virtual void showSpeak() { printf("Speak Chinese\r\n"); } };
int main(int argc, char* argv[]) { Person* p = new Chinese; p->~Person(); p = new (p) Chinese(); delete p;
return 0; }
|
22: p->~Person(); //显式调用析构函数 0000000140001772 mov rax,qword ptr [p] ;rax=对象地址 0000000140001777 mov rax,qword ptr [rax] ;rax=虚表指针 000000014000177A xor edx,edx ;参数0只调用析构函数不释放 000000014000177C mov rcx,qword ptr [p] ;this指针 0000000140001781 call qword ptr [rax] ;虚表第一项:代理析构函数 23: //将堆内存中p指向的地址作为Chinese的新对象的首地址,并调用Chinese的构造函数。这 24: //样可以重复使用同一个堆内存,以节约内存空间 25: p = new (p) Chinese(); 0000000140001783 mov rdx,qword ptr [p] 0000000140001788 mov ecx,8 000000014000178D call operator new (0140001203h) 0000000140001792 mov qword ptr [rsp+48h],rax 0000000140001797 mov rcx,qword ptr [rsp+48h];this指针 000000014000179C call Chinese::Chinese (01400011CCh) 00000001400017A1 mov qword ptr [p],rax
|
000000014000178D call operator new (0140001203h)
这里new也不是完整的new
167: _Writable_bytes_(_Size) void* _Where) noexcept 168: { 00000001400015C0 48 89 54 24 10 mov qword ptr [rsp+10h],rdx 00000001400015C5 48 89 4C 24 08 mov qword ptr [rsp+8],rcx 169: (void)_Size; 170: return _Where; 00000001400015CA 48 8B 44 24 10 mov rax,qword ptr [rsp+10h] 171: }
|
直接把堆返回回来了
多重继承
#include <stdio.h>
class Sofa { public: Sofa() { color = 2; }
virtual ~Sofa() { printf("virtual ~Sofa()\n"); }
virtual int getColor() { return color; } virtual int sitDown() { return printf("Sit down and rest your legs\r\n"); } protected: int color; };
class Bed { public: Bed() { length = 4; width = 5; }
virtual ~Bed() { printf("virtual ~Bed()\n"); }
virtual int getArea() { return length * width; }
virtual int sleep() { return printf("go to sleep\r\n"); } protected: int length; int width; };
class SofaBed : public Sofa, public Bed { public: SofaBed() { height = 6; }
virtual ~SofaBed() { printf("virtual ~SofaBed()\n"); }
virtual int sitDown() { return printf("Sit down on the sofa bed\r\n"); }
virtual int sleep() { return printf("go to sleep on the sofa bed\r\n"); }
virtual int getHeight() { return height; } protected: int height; };
int main(int argc, char* argv[]) { SofaBed sofabed; Sofa* sofa = &sofabed; Bed* bed = &sofabed; return 0; }
|
数据成员的排列顺序由继承父类的顺序决定,从左向右依次排列。
子类中实现的虚函数会在虚表里替换,没实现的依旧是父类虚函数
vs2022里的监视窗口不是真实内存布局,只不过这次凑巧一样,还是要从内存窗口看
构造函数会把this指针调整到各个父类部分,调用父类的构造函数,然后再把SofaBed的虚表指针赋值到各个父类部分
.text:0000000140001620 ; __unwind { // __CxxFrameHandler4_0 .text:0000000140001620 mov [rsp+arg_0], rcx .text:0000000140001625 sub rsp, 28h .text:0000000140001629 mov rcx, [rsp+28h+arg_0] ; this .text:000000014000162E call j_??0Sofa@@QEAA@XZ ; Sofa::Sofa(void) .text:000000014000162E .text:0000000140001633 nop .text:0000000140001634 mov rax, [rsp+28h+arg_0] .text:0000000140001639 add rax, 10h ;调整到Bed this位置 .text:000000014000163D mov rcx, rax ; this .text:0000000140001640 call j_??0Bed@@QEAA@XZ ; Bed::Bed(void) .text:0000000140001640 .text:0000000140001645 mov rax, [rsp+28h+arg_0] .text:000000014000164A lea rcx, ??_7SofaBed@@6BSofa@@@ ; const SofaBed::`vftable'{for `Sofa'} .text:0000000140001651 mov [rax], rcx .text:0000000140001654 mov rax, [rsp+28h+arg_0] .text:0000000140001659 lea rcx, ??_7SofaBed@@6BBed@@@ ; const SofaBed::`vftable'{for `Bed'} .text:0000000140001660 mov [rax+10h], rcx .text:0000000140001664 mov rax, [rsp+28h+arg_0] .text:0000000140001669 mov dword ptr [rax+20h], 6 .text:0000000140001670 mov rax, [rsp+28h+arg_0] .text:0000000140001675 add rsp, 28h .text:0000000140001679 retn .text:0000000140001679 ; } // starts at 140001620
|
析构函数也是一样套路,不过是先把SofaBed的虚表指针赋值到各个父类部分调用析构函数,在把this指针调整到各个父类部分,调用父类的构造函数
指针赋值也是一样的套路
抽象类
#include <stdio.h> class AbstractBase { public: AbstractBase() { printf("AbstractBase()"); } virtual void show() = 0; };
class VirtualChild : public AbstractBase { public: virtual void show() { printf("抽象类分析\n"); } };
int main(int argc, char* argv[]) { VirtualChild obj; obj.show(); return 0; }
|
纯虚函数的虚表项为_purecall_0,是编译器保证未定义的纯虚函数被调用提供的用于结束程序的函数
.rdata:0000000140007BB8 DE 15 00 40 01 00 00 00 00 00+??_7AbstractBase@@6B@ dq offset _purecall_0, 0
|
虚继承(菱形继承)
从这位大哥:https://lonelyenderman.top/archives/723 偷来的图,感觉比较有意思
羊和驼都继承了动物的类成员,当羊驼想要使用时,会产生数据冗余和二义性,引出了虚继承
#include <stdio.h>
class Furniture { public: Furniture() { printf("Furniture::Furniture()\n"); price = 0; } virtual ~Furniture() { printf("Furniture::~Furniture()\n"); }
virtual int getPrice() { printf("Furniture::getPrice()\n"); return price; }; protected: int price; };
class Sofa : virtual public Furniture { public: Sofa() { printf("Sofa::Sofa()\n"); price = 1; color = 2; } virtual ~Sofa() { printf("Sofa::~Sofa()\n"); } virtual int getColor() { printf("Sofa::getColor()\n"); return color; } virtual int sitDown() { return printf("Sofa::sitDown()\n"); } protected: int color; };
class Bed : virtual public Furniture { public: Bed() { printf("Bed::Bed()\n"); price = 3; length = 4; width = 5; }
virtual ~Bed() { printf("Bed::~Bed()\n"); }
virtual int getArea() { printf("Bed::getArea()\n"); return length * width; }
virtual int sleep() { return printf("Bed::sleep()\n"); } protected: int length; int width; };
class SofaBed : public Sofa, public Bed { public: SofaBed() { printf("SofaBed::SofaBed()\n"); height = 6; } virtual ~SofaBed() { printf("SofaBed::~SofaBed()\n"); }
virtual int sitDown() { return printf("SofaBed::sitDown()\n"); }
virtual int sleep() { return printf("SofaBed::sleep()\n"); }
virtual int getHeight() { printf("SofaBed::getHeight()\n"); return height; } protected: int height; };
int main(int argc, char* argv[]) { SofaBed sofabed; Furniture* p1 = &sofabed; Sofa* p2 = &sofabed; Bed* p3 = &sofabed; printf("%p %p %p\n", p1, p2, p3); return 0; }
|
sofabed内存布局
.text:0000000140003550 ; __int64 __fastcall main(int argc, char **argv) .text:0000000140003550 main proc near ; CODE XREF: main_0↑j .text:0000000140003550 ; DATA XREF: .pdata:000000014000B390↓o .text:0000000140003550 .text:0000000140003550 var_88= dword ptr -88h .text:0000000140003550 var_80= qword ptr -80h .text:0000000140003550 var_78= qword ptr -78h .text:0000000140003550 var_70= qword ptr -70h .text:0000000140003550 var_68= qword ptr -68h .text:0000000140003550 var_60= qword ptr -60h .text:0000000140003550 SofaBed= SofaBed ptr -58h .text:0000000140003550 arg_0= dword ptr 8 .text:0000000140003550 arg_8= qword ptr 10h .text:0000000140003550 .text:0000000140003550 mov [rsp+arg_8], rdx .text:0000000140003555 mov [rsp+arg_0], ecx .text:0000000140003559 sub rsp, 0A8h .text:0000000140003560 mov edx, 1 ; 构造虚基类的标志:1构造,0不构造 .text:0000000140003565 lea rcx, [rsp+0A8h+SofaBed] ; this .text:000000014000356A call j_??0SofaBed@@QEAA@XZ ; SofaBed::SofaBed(void) .text:000000014000356A .text:000000014000356F lea rax, [rsp+50h] .text:0000000140003574 test rax, rax .text:0000000140003577 jnz short loc_140003584 .text:0000000140003577 .text:0000000140003579 mov [rsp+0A8h+var_80], 0 .text:0000000140003582 jmp short loc_140003597 .text:0000000140003582 .text:0000000140003584 ; --------------------------------------------------------------------------- .text:0000000140003584 .text:0000000140003584 loc_140003584: ; CODE XREF: main+27↑j .text:0000000140003584 mov rax, qword ptr [rsp+0A8h+SofaBed.baseclass_0.gap8] ; 取出对象Sofa虚基类偏移表 .text:0000000140003589 movsxd rax, dword ptr [rax+4] ; 取出对象中Sofa类虚基类偏移表第二项,虚基类对象首地址相对于虚基类偏移表的偏移值。 .text:000000014000358D lea rax, [rsp+rax+0A8h+SofaBed.baseclass_0.gap8] ; 找到虚基类 .text:0000000140003592 mov [rsp+0A8h+var_80], rax .text:0000000140003592 .text:0000000140003597 .text:0000000140003597 loc_140003597: ; CODE XREF: main+32↑j .text:0000000140003597 mov rax, [rsp+0A8h+var_80] .text:000000014000359C mov [rsp+0A8h+var_60], rax ; Furniture* p1 = &sofabed; //转换成虚基类指针 .text:00000001400035A1 lea rax, [rsp+0A8h+SofaBed] .text:00000001400035A6 mov [rsp+0A8h+var_68], rax ; Sofa* p2 = &sofabed; //转换成父类指针 .text:00000001400035AB lea rax, [rsp+0A8h+SofaBed] .text:00000001400035B0 test rax, rax .text:00000001400035B3 jz short loc_1400035C5 .text:00000001400035B3 .text:00000001400035B5 lea rax, [rsp+0A8h+SofaBed] .text:00000001400035BA add rax, 18h ; 直接加偏移获取到Bed首地址 .text:00000001400035BE mov [rsp+0A8h+var_78], rax .text:00000001400035C3 jmp short loc_1400035CE .text:00000001400035C3 .text:00000001400035C5 ; --------------------------------------------------------------------------- .text:00000001400035C5 .text:00000001400035C5 loc_1400035C5: ; CODE XREF: main+63↑j .text:00000001400035C5 mov [rsp+0A8h+var_78], 0 .text:00000001400035C5 .text:00000001400035CE .text:00000001400035CE loc_1400035CE: ; CODE XREF: main+73↑j .text:00000001400035CE mov rax, [rsp+0A8h+var_78] .text:00000001400035D3 mov [rsp+0A8h+var_70], rax ; Bed* p3 = &sofabed; //转换成父类指针 .text:00000001400035D8 mov r9, [rsp+0A8h+var_70] .text:00000001400035DD mov r8, [rsp+0A8h+var_68] .text:00000001400035E2 mov rdx, [rsp+0A8h+var_60] .text:00000001400035E7 lea rcx, aPPP ; "%p %p %p\n" .text:00000001400035EE call j_printf .text:00000001400035EE .text:00000001400035F3 mov [rsp+0A8h+var_88], 0 .text:00000001400035FB lea rcx, [rsp+0A8h+SofaBed] ; this .text:0000000140003600 call j_??_DSofaBed@@QEAAXXZ ; SofaBed::`vbase destructor'(void) .text:0000000140003600 .text:0000000140003605 mov eax, [rsp+0A8h+var_88] .text:0000000140003609 add rsp, 0A8h .text:0000000140003610 retn
|
Sofa虚基类偏移表存储两个值-8 和 0x30
- 第一项为-8,即虚基类偏移表所属类对应的对象首地址相对于虚基类偏移表的偏移值;
- 第二项保存的是虚基类对象首地址相对于虚基类偏移表的偏移值。
虚基类放到了一个公共的位置,羊和驼类里原本存放动物的位置现在存放虚基偏移表指针,利用偏移来寻址虚基类
多个虚基类时,会在虚基类偏移表中依次记录它们的偏移量。
虚继承子类构造函数
虚继承子类构造函数传入了参数1:this指针,参数2:构造虚基类的标志:1构造,0不构造,防止重复构造虚基类
.text:0000000140001860 ; void __fastcall SofaBed::SofaBed(SofaBed *this) .text:0000000140001860 ??0SofaBed@@QEAA@XZ proc near ; CODE XREF: SofaBed::SofaBed(void)↑j .text:0000000140001860 ; DATA XREF: .pdata:000000014000B048↓o .text:0000000140001860 .text:0000000140001860 var_18= dword ptr -18h .text:0000000140001860 sofabed_this= qword ptr 8 .text:0000000140001860 flag= dword ptr 10h .text:0000000140001860 .text:0000000140001860 ; FUNCTION CHUNK AT .text:00000001400057E0 SIZE 0000002A BYTES .text:0000000140001860 ; FUNCTION CHUNK AT .text:0000000140005810 SIZE 0000001C BYTES .text:0000000140001860 .text:0000000140001860 ; __unwind { // __CxxFrameHandler4_0 .text:0000000140001860 mov [rsp+flag], edx .text:0000000140001864 mov [rsp+sofabed_this], rcx .text:0000000140001869 sub rsp, 38h .text:000000014000186D mov [rsp+38h+var_18], 0 .text:0000000140001875 cmp [rsp+38h+flag], 0 .text:000000014000187A jz short loc_1400018B9 ; 为0不构造虚基类,防止重复构造虚基类 .text:000000014000187A .text:000000014000187C mov rax, [rsp+38h+sofabed_this] ; rax=this指针 .text:0000000140001881 lea rcx, ??_8SofaBed@@7BSofa@@@ ; const SofaBed::`vbtable'{for `Sofa'} .text:0000000140001888 mov [rax+8], rcx ; 设置父类Sofa中的虚基类偏移表 .text:000000014000188C mov rax, [rsp+38h+sofabed_this] .text:0000000140001891 lea rcx, ??_7SofaBed@@6BFurniture@@@+10h ; const SofaBed::`vftable'{for `Furniture'} .text:0000000140001898 mov [rax+20h], rcx ; 设置父类Bed中的虚基类偏移 .text:000000014000189C mov rax, [rsp+38h+sofabed_this] .text:00000001400018A1 add rax, 38h ; '8' ; 调整this指针为虚基类this指针 .text:00000001400018A5 mov rcx, rax ; this .text:00000001400018A8 call j_??0Furniture@@QEAA@XZ ; Furniture::Furniture(void) .text:00000001400018A8 .text:00000001400018AD nop .text:00000001400018AE mov eax, [rsp+38h+var_18] ; eax=0 .text:00000001400018B2 or eax, 1 ; eax=1 .text:00000001400018B5 mov [rsp+38h+var_18], eax .text:00000001400018B5 .text:00000001400018B9 .text:00000001400018B9 loc_1400018B9: ; CODE XREF: SofaBed::SofaBed(void)+1A↑j .text:00000001400018B9 xor edx, edx ; 给Sofa传入0,不再构造虚基类,上面已经构造过了 .text:00000001400018BB mov rcx, [rsp+38h+sofabed_this] ; this .text:00000001400018BB .text:00000001400018C0 .text:00000001400018C0 loc_1400018C0: ; CODE XREF: .text:00000001400010DC↑j .text:00000001400018C0 call j_??0Sofa@@QEAA@XZ ; Sofa::Sofa(void) .text:00000001400018C0 .text:00000001400018C5 nop .text:00000001400018C6 ; try { .text:00000001400018C6 mov rax, [rsp+38h+sofabed_this] .text:00000001400018CB add rax, 18h .text:00000001400018CF xor edx, edx .text:00000001400018D1 mov rcx, rax ; this .text:00000001400018D4 call j_??0Bed@@QEAA@XZ ; Bed::Bed(void) .text:00000001400018D4 .text:00000001400018D9 mov rax, [rsp+38h+sofabed_this] ; 下面就是设置各个类的虚表指针 .text:00000001400018DE lea rcx, ??_7SofaBed@@6BSofa@@@ ; const SofaBed::`vftable'{for `Sofa'} .text:00000001400018E5 mov [rax], rcx .text:00000001400018E8 mov rax, [rsp+38h+sofabed_this] .text:00000001400018ED lea rcx, ??_7SofaBed@@6BBed@@@ ; const SofaBed::`vftable'{for `Bed'} .text:00000001400018F4 mov [rax+18h], rcx .text:00000001400018F8 mov rax, [rsp+38h+sofabed_this] .text:00000001400018FD mov rax, [rax+8] .text:0000000140001901 movsxd rax, dword ptr [rax+4] .text:0000000140001905 mov rcx, [rsp+38h+sofabed_this] .text:000000014000190A lea rdx, ??_7SofaBed@@6BFurniture@@@ ; const SofaBed::`vftable'{for `Furniture'} .text:0000000140001911 mov [rcx+rax+8], rdx .text:0000000140001916 lea rcx, aSofabedSofabed ; "SofaBed::SofaBed()\n" .text:000000014000191D call j_printf .text:000000014000191D .text:0000000140001922 mov rax, [rsp+38h+sofabed_this] .text:0000000140001927 mov dword ptr [rax+30h], 6 .text:0000000140001927 ; } // starts at 1400018C6 .text:000000014000192E mov rax, [rsp+38h+sofabed_this] .text:0000000140001933 add rsp, 38h .text:0000000140001937 retn
|
虚继承子类代理析构函数
.text:00000001400019F0 ; void __fastcall SofaBed::`vbase destructor'(SofaBed *this) .text:00000001400019F0 ??_DSofaBed@@QEAAXXZ proc near ; CODE XREF: SofaBed::`vbase destructor'(void)↑j .text:00000001400019F0 ; DATA XREF: .pdata:000000014000B060↓o .text:00000001400019F0 ; .pdata:000000014000B06C↓o .text:00000001400019F0 .text:00000001400019F0 sofabed_this= qword ptr 8 .text:00000001400019F0 .text:00000001400019F0 mov [rsp+sofabed_this], rcx .text:00000001400019F5 sub rsp, 28h .text:00000001400019F9 mov rax, [rsp+28h+sofabed_this] .text:00000001400019FE add rax, 38h ; '8' .text:0000000140001A02 mov rcx, rax ; this .text:0000000140001A05 call j_??1SofaBed@@UEAA@XZ ; SofaBed::~SofaBed(void) .text:0000000140001A05 .text:0000000140001A0A mov rax, [rsp+28h+sofabed_this] .text:0000000140001A0F add rax, 38h ; '8' .text:0000000140001A13 mov rcx, rax ; this .text:0000000140001A16 call j_??1Furniture@@UEAA@XZ ; Furniture::~Furniture(void) .text:0000000140001A16 .text:0000000140001A1B add rsp, 28h .text:0000000140001A1F retn
|
先依次执行两个父类Bed和Sofa的析构函数,然后执行虚基类的析构函数
Reference
C++反汇编与逆向分析技术揭秘