We all know that the implementation of virtual functions in C ++ is generally implemented through the virtual function table (the C ++ specification does not specify which method to use, but most compiler vendors choose this method ), the following describes the implementation of the virtual function in C ++ through the virtual function table.
Class
{
Public:
Int ai;
Virtual void func () {cout <"A-func" <endl ;}
};
Class AA: public
{
Public:
Void func () {cout <"AA-func" <endl ;}
};
Int main ()
{
AA * paa = new AA;
A * pa = new;
}
We can see thatPaaPointAAIts Sub-objectsAVirtual function table(Vftb)The address is0x0047e69c, ExactlyAAFirst object32Bit
If you change the class definition:
Class
{
Public:
Int ai;
Virtual void func () {cout <"A-func" <endl ;}
};
Class AA: public
{
Public:
// Void func () {cout <"AA-func" <endl;} removes the override of this virtual function in AA.
};
Then we can see the first item in the virtual function table in paa (that is, the func inherited from A), and the first item in the virtual function table in pa (func in ), point to the same place (0x004010f5), you can draw a conclusion:When the derived class does not overwrite(Override)When a virtual function in the base class is used, the virtual function refers to the implementation of the virtual function in the base class.. Similar concepts are also found in C #. The override keyword in C # indicates that the derived class needs to rewrite the point of the virtual function item in the base class.
Add a virtual func virtual function in the derived class.
Class
{
Public:
Int ai;
Virtual void func () {cout <"A-func" <endl ;}
Virtual void func2 () {cout <"A-func2" <endl ;}
};
Class AA: public
{
Public:
Void func () {cout <"AA-func" <endl ;}
Virtual void aa_func () {cout <"AA-aa_func" <endl ;}
};
For this code, the debugger of VS2005 does not correctly reflect the location of aa_func in AA (the vfptr virtual function table contains only two items). However, we can still see from the figure that paa-> aa_func occupies the third position in the paa->__ vfptr table (vfptr + 8 is the third item in the table, the offset of each table item is 4 bytes ). In addition, if the derived class does not overwrite the virtual function of the base class, it points to the same function body as the base class (both func2 point to A: func2)
Let's recall the type conversion process of C ++ in the previous article:
AA * paa = new AA;
At this time, paa points to an instance of AA)
A * pa = new AA;
At this time, pa points to the AA instance.
Therefore, pa-> func and paa-> func both point to the first item of vftb In the AA instance, that is, AA: func (), the reason why pointers can present polymorphism is self-evident. (The same is true for references)
Let's take a look at the assembly code of non-virtual and virtual functions:
Class
{
Public:
Int ai;
Virtual void func () {cout <"A-func" <endl ;}// check the assembly code with (none) virtual
};
Class AA: public
{
Public:
};
Non-virtual functions:
Paa-> A: func ();
004010C6 mov ecx, dword ptr [paa]
004010C9 add ecx, 4 // press the paa object to stack (offset 4 is because the first 4 bytes are saved in the vftb virtual function table)
004010CC call A: func (401046 h)
Virtual functions:
Paa-> func ();
00401230 mov edx, dword ptr [paa] // paa object-> edx
00401233 mov eax, dword ptr [edx] // vftb-> eax
00401235 mov ecx, dword ptr [paa] // paa object-> ecx
00401238 mov edx, dword ptr [eax] // The first item of vftb-> edx, that is, the position of func
0040123A call edx
After a series of "complex" calculations, the correct address of func is stored in the edx register, which is also called late-binding (late binding)
Finally, let's look at the memory layout of objects caused by virtual functions.
When there is no virtual function in the base class, there is no virtual function table in the natural base class. When there is a virtual function in the base class, the compiler generates a virtual function table, A derived class uses the virtual function table in its base class sub-object.
Class
{
Public:
Int ai;
Virtual void func2 () {cout <"A-func2" <endl ;}
};
Class AA: public
{
Public:
Void func () {cout <"AA-func" <endl ;}
Virtual void aa_func () {cout <"AA-aa_func" <endl ;}
};
The first item of Vftb is func2 () in A, and the second item is aa_func in AA.