Blog migrated to: http://kulv.sinaapp.com/
The reason why the base class constructor calls the virtual function is not the problem of the derived class
We know that the compiler inserts a lot of code in the constructor of the class, such as exception security, settings of the virtual function table pointer, and construction of the base class.
In addition, the key is that the Code is inserted before any user's code (non-initialization). The problem arises ···
If the base class constructor calls the base class virtual function, the actual calling is not as good as what we thought at the beginning. Why?
Let's take a look at how compilation is implemented ····
Below are the test classes:
Class A {<br/> Public: <br/> A () {<br/> vfunc (); <br/>}< br/> virtual vfunc () {<br/>;< br/>}< br/>}; </P> <p> class CC: Public A {<br/> public: <br/> virtual vfunc () {<br/>; <br/>}< br/>}; </P> <p> int main () <br/>{< br/> cc c; <br/> return 0; <br/>}< br/>
Cause:
004012f8 Lea ECx, [ebp-4] // load this pointer to ECx, Which is thiscall's research Convention <br/> 004012fb call @ ILT + 5 (CC: CC) (0040100a) // call the CC constructor <br/> 30: Return 0; <br/> 00401300 XOR eax, eax <br/> 31 :} <br/> //////////////////////////////////// /// // </P> <p> CC:: CC: <br/> · <br/> 00401339 pop ECx // This pointer to ECx <br/> 0040133a mov dword ptr [ebp-4], ECX <br/> 0040133d mov ECx, dword ptr [ebp-4] <br/> 00401340 Cal L @ ILT + 15 (A: a) (00401014) // After the constructor of the derived class enters, the base class constructor is called immediately after nothing is done, its vptr virtual function table pointer has no value! <Br/> 00401345 mov eax, dword ptr [ebp-4] <br/> 00401348 mov dword ptr [eax], offset CC: 'vftable' (00431068) // assign values to the pointer of the virtual function table until this is done. Therefore, the previous call will never involve the virtual function of CC in the derived class. <br/> 0040134e mov eax, dword ptr [ebp-4] // if there is any virtual function call afterwards, when accessing the virtual function table access is normal is the virtual function table of the derived class, <br/> 00401351 pop EDI <br/> 00401352 pop ESI <br/> 00401353 pop EBX <br/> 00401354 add ESP, 44 h <br/> 00401357 cmp ebp, esp <br/> 00401359 call _ chkesp (00401590) <br/> 0040135e mov ESP, EBP <br/> 00401360 pop EBP <br/> 00401361 RET <br/> ////////////////////// //////////////////////////////////////// //// // </P> <p> 10: A () {<br/> · <br/> 00401399 pop ECx <br/> 0040139a mov dword ptr [ebp-4], ECx <br/> 0040139d mov eax, dword ptr [ebp-4] <br/> 004013a0 mov dword ptr [eax], offset a: 'vftable' (004320.c) // notice that the first thing in the base class is: assign a value to the pointer address of the virtual function table. <br/> // This pointer is actually the address of the this pointer of the CC derived class, that is, the first of the class. Byte <br/> 11: vfunc (); // at this time, the this pointer will be passed in, and this vptr is not a derived class at this time, it is the virtual function table address of the base class set in the above sentence! <Br/> 004013a6 mov ECx, dword ptr [ebp-4] // pass this many people will "wrong thought" This <br/> 004013a9 call @ ILT + 10 (:: vfunc) (0040100f) // call this "virtual function". The virtual function table is not accessed here because of Compiler Optimization. It must be vfunc of, <br/> // if you are interested, try the following example: <br/> 12 :}< br/> · <br/> 004013c1 RET
I didn't access the virtual function table above because the compiler optimized it. If the Class A constructor is like this, then ···
Class A {<br/> Public: <br/> A () {<br/> Init (); // call a's non-virtual function first, call the vfunc virtual function to access the virtual function table, however, at this time, the virtual function table Pointer Points to the virtual function table of a <br/>}< br/> virtual Vir () {;}; // intentionally added one, the plus 4 words will be displayed. <br/> virtual vfunc () {// the second item of the virtual function table should be displayed. <br/>; <br/>}< br/> Init () {<br/> vfunc (); <br/>}< br/> }; </P> <p> 10: A () {<br/> · <br/> 004013a9 pop ECx <br/> 004013aa mov dword ptr [ebp-4], ECX <br/> 004013ad mov eax, dword ptr [ebp-4] <br/> 004013b0 mov DW Ord PTR [eax], offset a: 'vftable' (00431074) // you can specify the virtual function table pointer as the base class !! It is not a derived class. After the call is completed, it will be overwritten by the derived class <br/> 11: Init (); <br/> 004013b6 mov ECx, dword ptr [ebp-4] <br/> 004013b9 call @ ILT + 40 (A: init) (0040102d) // not a virtual function, called directly, no need to access the virtual function table <br/> 12 :} <br/> · <br/> 004013d1 RET </P> <p> /////////////////// //////////////////////////////////////// /// // <br/> 18: init () {<br/> · <br/> 00401469 pop ECx <br/> 0040146a mov dword ptr [ebp-4], ECX // This pointer is placed on the top of the stack <br/> 19: vfunc (); <br/> 0040146d Mo V eax, dword ptr [ebp-4] // below to prepare the query of the virtual function table, we can see that the virtual function call mechanism will still use several addressing operations, access the memory. <Br/> 00401470 mov edX, dword ptr [eax] // This-> EDX. In fact, the content is the pointer to the virtual function table of a <br/> 00401472 mov ESI, esp <br/> 00401474 mov ECx, dword ptr [ebp-4] <br/> 00401477 call dword ptr [edX + 4] // vfunc in Item 2, so + 4, Here <br/> 0040147a cmp esi, ESP <br/> 0040147c call _ chkesp (00401610) <br/> 20 :} <br/> · <br/> 00401491 RET </P> <p>