Virtual table pointer
Someone in the group asked. I wrote it
Starting from the simplest class, there are differences between single inheritance and multi inheritance virtual table pointers
Code in 32 environment:
Write the conclusion first:
Virtual table pointer is assigned in constructor and destructor (disassembly proof below), assignment: * this = virtual table pointer (i.e. the first member)
The virtual table pointer points to an array to store virtual functions, which are sorted according to the general declaration
This array is usually in. rdata or. data
Each parent class will set its own virtual table in the construction and destruction, which will be written in the following inheritance
* Why should we assign values in both construction and destruction? It will be written in the following inheritance
The following code comes with some memory layouts
Virtual table pointer in constructor * this = virtual table pointer, pointing to array [virtual void t]
The member variable a is at this + 4 and follows the virtual table. If there is no virtual table, it will be placed on this
#include <iostream> #include <Windows.h> #include <stdio.h> using std::cout; using std::endl; class A { public: A() { a = 1; } ~A() { } virtual void t() { cout << "t" << endl; } int a; }; int main() { cout <<"size:" << sizeof(A) << endl; A a; DWORD * ptr = (DWORD*)&a; DWORD vptr_addr = *ptr; //The virtual table is at the address of the first member, * this cout << "Virtual table:" << std::hex << vptr_addr<< endl; // Because vptr occupies the position of the first member, member a follows the virtual table int member = *(ptr + 1); cout << "member:" << member << endl; //Find the function address through the virtual table. There is only one virtual function, so the offset is 0 DWORD * arr_func = (DWORD*)vptr_addr; void (*func)() = (void (*)())(*(arr_func + 0)); // arr_func[0] cout << "Function address:" << func << endl; func(); return 0; }
Look at the construction destructor of disassembly, delete irrelevant ones, and only look at the assignment of virtual table pointer in construction and Deconstruction:
In these two functions, the assignment is related to inheritance to prevent calling the wrong virtual function
A(){ mov dword ptr [this],ecx // ecx is this mov eax,dword ptr [this] // eax = this // *This = virtual table pointer of a, assignment here mov dword ptr [eax],offset A::`vftable' (0419B34h) } Virtual table pointer: 0x00419B34 Memory data: 0x00419B34 9a 11 41 00 00 00 00 00 Restore array: [0x0041119a] -> [virtual t] // It also appears in the destruct ~A(){ mov dword ptr [this],ecx // ecx == this mov eax,dword ptr [this] // eax = this // *This = virtual table pointer of a mov dword ptr [eax],offset A::`vftable' (0419B34h) }
vptr here is 0x00419B34, the first function address is 0x0041119a - > the address of virtual function t;
Single inheritance:
The parent class will point its own virtual table pointer of * this = parent class to its own array in the constructor, [virtual ~A, virtual A::t]
The subclass will put * this in the construction = Virtual table pointer of subclass, array [virtual ~B, virtual A::t]
Virtual functions are generally sorted in the order of declaration in the array
Data members are arranged in succession order, i.e. virtual tables, a and B
Set * this = parent virtual table in parent class construction and destruct to prevent errors in calling virtual functions
Now suppose:
1. Subclass, if any virtual void t, then subclass virtual table array: [virtual ~B, virtual B::t]
two If t() is called during parent class construction and destruction, and * this = parent virtual table is not set,
The parent class will call the virtual void t of the child class
#include <iostream> #include <Windows.h> #include <stdio.h> using std::cout; using std::endl; class A { public: A() { a = 1; } virtual ~A() { // Virtual deconstruction } virtual void t() { cout << "t" << endl; } int a; }; class B : public A { public: B() { b = 2; } ~B() { } int b; }; int main() { cout <<"size:" << sizeof(B) << endl; B b; DWORD * ptr = (DWORD*)&b; DWORD vptr_addr = *ptr; cout << "Virtual table:" <<std::hex <<vptr_addr << endl; //The reason for obtaining members and inheriting order: A's things are put in the front and his own things are put in the back int member_in_class_a = *(ptr + 1); int member_in_class_b = *(ptr + 2); cout << "A::a :" << member_in_class_a << ", B::b:" << member_in_class_b << endl; // Get the virtual table pointer of subclass and call function T. the first is destructor and the second function is t DWORD * arr_func = (DWORD*)vptr_addr; void(*func)() = (void(*)())(arr_func[1]); cout << "Function address:" << std::hex <<func << endl; func(); return 0; }
Disassembly, constructor, remove irrelevant:
A(){ mov dword ptr [this],ecx // ecx = this pointer mov eax,dword ptr [this] // eax = this // *this = parent virtual table pointer 0x00419B34 mov dword ptr [eax],offset A::`vftable' (0419B34h) } A Virtual table address of: 0x00419B34 A Virtual table memory data:0x00419B34 65 14 41 00 60 14 41 00 Restore array:[0x00411465,0x00411460] -> [virtual ~A , virtual A::t ] corresponding VSdebug: 0x00411465 {virtualptr.exe!A::`vector deleting destructor'(unsigned int)} // Destruct ~ A 0x00411460 {virtualptr.exe!A::t(void)} // A::t B() { mov ecx,dword ptr [this] // eax = this call A::A (041146Fh) // Call parent class construction mov eax,dword ptr [this] // eax = this // *this = subclass virtual table pointer 0x00419B44 mov dword ptr [eax],offset B::`vftable' (0419B44h) } B Virtual table address of:0x00419B44 Virtual table memory data:0x00419B44 51 14 41 00 60 14 41 00 array:[0x00411451, 00411460] -> [virtual ~B , virtual A::t] corresponding VS debug: 0x00411451 {virtualptr.exe!B::`vector deleting destructor'(unsigned int)} // Destruct ~ B 0x00411460 {virtualptr.exe!A::t(void)} // A::t
You can see:
The parent virtual table points to its own array: [virtual ~A, virtual A::t]
Subclass points to its own array: [virtual ~B, virtual A:t]
Prevent the parent class from calling subclass virtual functions
Destructor disassembly is also the same operation to prevent subclass virtual functions from being destructed.
~B() { mov dword ptr [this],ecx // ecx == this mov eax,dword ptr [this] // eax = this // *This = virtual table pointer of B mov dword ptr [eax],offset B::`vftable' (0419B44h) // ecx = this mov ecx,dword ptr [this] // Call parent class destructor call A::~A (041144Ch) } ~A(){ mov dword ptr [this],ecx // ecx == this // eax = this mov eax,dword ptr [this] // *This = virtual table of a mov dword ptr [eax],offset A::`vftable' (0419B34h) }
Do a test?
Now suppose there is another base class:
class Base { public: Base() { base = 111; } virtual ~Base() { } int base; }; class A : public Base{ //Same as above } class B: public A{ //Same as above }
At this time, there will be three virtual tables, and the construction and destruction of Base will also assign a value to Base's own virtual table;
Base virtual table - > [virtual ~ base]
A virtual table - > [virtual ~ A, virtual A:: T]
B virtual table - > [virtual ~ B, virtual A:: T]
Memory layout, as mentioned above, is arranged by inheritance:
First address: virtual table, Base::base, A::a, B::b
If Base is changed to this, there is no virtual function:
class Base { public: Base() { base = 111; } ~Base() { } int base; };
At this time, the. Base has no virtual table, and its construction and destruction will not assign its own virtual table
A virtual table and B virtual table remain unchanged
The memory layout remains the same
For example:
If int main(){ B b; // &b == 0x0018FEE4 b Memory data for 0x0018FEE4 48 9b 41 00 6f 00 00 00 01 00 00 00 02 00 00 00 vs debug Corresponding data: + __vfptr 0x00419b48 {virtualptr.exe!void(* B::`vftable'[3])()} base 0x0000006f int a 0x00000001 int b 0x00000002 int }
Multiple inheritance: