Record several questions about how to compile and process this pointer and multiple virtual tables in the C ++ multi-inheritance process to check the behavior of the compiler.
Simple and unreasonable test code:
# Include
# Include
Using namespace std; class A {public: int x; int y; public: A () {cout <the value of this pointer passed by constructor A is: <
X = 1; this-> y = 2; cout <z = 10; cout <the value of this pointer passed in by constructor C is: <z = 1; // analyze the inherited function relationships and determine the position a = (int) this;} void F4 () {this-> z = 1; a = (int) this ;}}; void diaplay (A * a) {a-> F1 ();} typedef void (C: * pFun) (void); int main (int argc, char ** argv) {// A * pc = new C (); // A * pd = new D (); A * pc = new C (); C * pc1 = new C (); A * pd = new D (); D * pd1 = new D (); // diaplay (pc ); // diaplay (pd); // pc-> F1 (); // pd-> F1 (); pd1-> F3 (); // switch to pd1> F4 (); cin> argc; return 0 ;}
Simple and unreasonable test code:
Class A has an F1 and F3 virtual functions, where F3 is A pure virtual function.
Class B has an F2 virtual function
The inheritance sequence of class C is A and B, while that of class D is B and.
Then, the virtual table pointer storage location of objects in Class C and D is the opposite, for example.
Questions and summary:
1. How to call the member functions of a derived class in the base class
It can be implemented through the virtual function method. Pure virtual function is the best. Call the derived class in the member function of the base class to execute the implementation function of the derived class.
In essence, it must be noted that the virtual table pointer is used for scheduling. Currently, this can only access the member variables, functions, and a virtual table of the base class, functions in a virtual table can be located by the compiler through the positions declared in the base class. Therefore, no matter where the virtual function is located in the virtual table, this can be determined at the compilation stage, for static compilation. For a specific virtual table, it is dynamically running with the establishment of this, that is, the nature of C ++ polymorphism, and delayed execution. In essence, it is still static compilation. You only use this to access the virtual table. After the virtual table is offset, determine the function entry.
It should be noted that the call stored in the virtual table is not necessarily the direct entry for function execution, but the entry address of some column function export symbol tables, internally, the jump is used to go to the actual function execution entry, which is determined by the compiler. I guess it is a function that can be quickly linked to the external module through the symbol table, bind the function entry through the symbol table name, and then add the function entry address in the executable file to the symbol table to complete initialization.
2. If the base class has a base class virtual table, the value of this should be the location of the virtual table, that is, the zero address location of each base class object.
3. Memory layout under multi-inheritance under C ++
Each base class needs to occupy a memory area. For the base class containing virtual functions, the heap of the object memory needs to contain a base class virtual table pointer, the functions in the virtual table are overwritten by the derived class based on the override relationships inherited by the class.
4. this pointer is used flexibly in Multi-inheritance. The actual this pointer is different between the base class and the function of the derived class.
First, the compiler calls the constructor of the base class and derived class in sequence when generating the object. For example, in class C and Class D, during the construction of base class A and B objects, the passed-in this pointer value is inconsistent with the object of the derived class. The compiler has done this. For Class C, if this is 0xxxx00, the Class A constructor imports 0xxxx00 because the two are in the same space location in the memory distribution. For Class D, if this is 0xxxx00, the value of this passed to Class A constructor is 0xxxx08, the offset is the size of the virtual table pointer and member variable occupied by Class B.
So every time you compile a base-class function, the compiler pushes the address of the base-class memory object to the stack frame as the this value in the current function, the fundamental goal is that, once the value of this in the current base class is determined, the compiler can call the virtual table function (whether or not it is overwritten by the derived class) and the member variable based on the virtual table location.
5. How does the compiler handle non-inherited virtual functions and inherited virtual or pure virtual functions in a derived class?
Similar to F3 and F4 in the above D class, we can know through the assembly code that this in F3 is still 0xxxx08, and F4 is indeed 0xxxx00. the essential purpose is that the compiler knows that F3 inherits the base class A, while the virtual table of the base class A is located at 0xxxx08, therefore, the input this pointer needs to adjust the this value of the derived class object.
For F4, the actual input value of this is 0xxxx00.
For the above two identical code logics, the compiler is indeed processing differently:
void F4(){00CE20F0 push ebp 00CE20F1 mov ebp,esp 00CE20F3 sub esp,0CCh 00CE20F9 push ebx 00CE20FA push esi 00CE20FB push edi 00CE20FC push ecx 00CE20FD lea edi,[ebp-0CCh] 00CE2103 mov ecx,33h 00CE2108 mov eax,0CCCCCCCCh 00CE210D rep stos dword ptr es:[edi] 00CE210F pop ecx 00CE2110 mov dword ptr [ebp-8],ecx this->z = 1;00CE2113 mov eax,dword ptr [this] 00CE2116 mov dword ptr [eax+4],1 a = (int)this;00CE211D mov eax,dword ptr [this] 00CE2120 mov ecx,dword ptr [this] 00CE2123 mov dword ptr [eax+14h],ecx }
Assign a value to the member variable of the base class B. F4 uses the this value of the current derived class to eax, and eax + 4 is the position of z, that is, the this value of the derived class object is passed in.
Virtual void F3 () {00CE20A0 push ebp 00CE20A1 mov ebp, esp 00CE20A3 sub esp, 0CCh 00CE20A9 push ebx 00CE20AA push esi 00CE20AB push edi 00CE20AC push ecx 00CE20AD lea edi, [ebp-0CCh] 00CE20B3 mov ecx, 33 h 00CE20B8 mov eax, 0 CCCCCCCCh 00CE20BD rep stos dword ptr es: [edi] 00CE20BF pop ecx 00CE20C0 mov dword ptr [ebp-8], ecx this-> z = 1; // analyze the inherited function relationships and determine their location 00CE20C3 mov eax, dword ptr [this] 00CE20C6 mov dword ptr [eax-4], 1 a = (int) this; 00CE20CD mov eax, dword ptr [this] 00CE20D0 sub eax, 8 00CE20D3 mov ecx, dword ptr [this] 00CE20D6 mov dword ptr [ecx + 0Ch], eax}
In assigning a value to the member variable of base class B, F3 uses the this value of the current base class to eax, where the eax-4 is z, that is, it is clear that the value of this should be the location of the base class A virtual table.
The difference between the two lies in that the former F4 is a non-inherited virtual function, while the latter is an inherited virtual function, and the compiler processes the function differently, the inheritance process must follow the base class because the base class is the basis for derivation.
6. Compile and automatically identify inheritance relationships, such as F3 calls in the base class.
The value of this pointer passed in to F3 is still. When you need to read and write this, the compiler automatically adds the code to change this pointer to the address of the derived class. Therefore, whether it is a base class pointer or a derived class pointer, if it points to a derived class object, the current function this value theory in the stack frame is directed to the base class, which is the behavior of the compiler, the internal processing of the function is consistent with that of the base class pointer. Therefore, when executing F3 for a pointer to a derived class, you still need to call the virtual table. First, convert this value to the address of the base class and pass it into the stack frame as a parameter.
7 different types of inheritance and derivation relationships, this pointer is essentially a dynamic process in the transfer process, but it should be clear that each object has a this pointer, this is passed between functions in the form of an input parameter in the stack frame.