1. Generic class without inheritance:
In the case of a virtual function, the class adds a hidden member, a virtual function table pointer to a virtual function table, and the virtual function table is the address of each virtual function of the class. So, what model is the virtual function table pointer added to the class, and how is the virtual function table arranged? Let's see it in a nutshell.
#include"stdafx.h" #pragmaPack (8)classa{ Public: intADoubleA2; A (): A (0xaaaaaaaa), A2 (0){} Virtual voidfunA2 () {}Virtual~A () {}Virtual voidFuna () {}}; int_tmain (intARGC, _tchar*argv[]) {A A; return 0; }
Define a variable and look at its memory layout:
The first 4 bytes are virtual function table pointers, Class A has a double type of member variable A2, so the valid byte alignment of Class A is 8, so you can see that the virtual function table pointer is padded with 4 bytes. Complete the virtual function table pointer before you go to the member variable of class A. So in the ordinary class, if there is a virtual function, the first place to add a hidden member variable, virtual function table pointer, and then to the normal member variable. And then we'll see what it looks like in the virtual function table:
A virtual function table is also an address that holds a virtual function for each item in 4 bytes. The address of the saved virtual function is discharged in the order declared by the function, the first one holds the virtual function of the first declaration, the second item holds the second one, and so on. Let's see what each item in this table IS.
Select: Debug--Window----Disassembly, open assembly window, you can see the assembly code of the source program.
Let's take a look at the first virtual function:
Virtual void funA2 () {}
From the above, the address of the function is: 0x00d41028 (note is the small end order), the address is found in the assembly window:
See 0x00d41028 Place a jmp instruction, virtual void funA2 () The real address is 0x00d41550
We can find the 0x00d41550 address in the assembly window, the result is as follows:
You can see that each entry in this virtual function table is not actually the direct address of the virtual function, but rather a jump to the address of the corresponding virtual function.
Therefore, in the case of virtual functions, the arrangement of classes is very simple, and there is no virtual function compared to the first with a virtual function table pointer. The other thing is the same as the case of classes without virtual functions. And then there seems to be nothing then, the complex is in the back ~
2. Case of single inheritance:
Single inheritance can be divided into two situations, one is that the base class does not have virtual functions, one is that the base class already has a virtual function table pointer situation. Let's take a look at each other.
2.1 Single inheritance of base class without virtual function
#include"stdafx.h" #pragmaPack (8)classf2{ Public:intF2;DoubleF22; F2 (): F2 (0XF2F2F2F2), F22 (0){} }; classb:f2{ Public: intb; B (): B (0xbbbbbbbb){} Virtual voidFunb () {}}; int_tmain (intARGC, _tchar*argv[]) {b b; return 0; }
The layout of B is captured with the following data:
You can see the virtual function table pointer or the first place, follow its own address alignment rules, and actively populate the 4 bytes at the back. Then it is F2 as a whole structure is stored behind, and finally the member variable B, the entire structure also to align itself, so padding 4 bytes at the end. Inside the virtual function table is the address of the virtual function Funb of B. Because there is only one virtual function, there is only one item in the Virtual function table.
Again, we open the Disassembly window and find the 0x012e1221 address:
You can see that a jmp instruction is placed at 0x012e1221, and the real address of virtual void Funb () {} is 0x012e14e0
We can find the 0X012E14E0 address in the assembly window, the result is as follows:
This is the starting position of virtual void Funb () {} ~
Therefore, in the case of a base class without virtual functions, a virtual function table pointer is generated, and the virtual function table pointer of the class is stored first, and then to the base class. In fact, in the case of a virtual function of the class (temporarily regardless of virtual inheritance), virtual function table pointers will be stored at the beginning. Let's take a look at what happens if the inherited base class already has a virtual function table pointer.
2.2 Single inheritance for base classes with virtual functions
#include"stdafx.h" #pragmaPack (8)classA { Public: intADoubleA2; A (): A (0xaaaaaaaa), A2 (0){} Virtual voidfunA2 () {}Virtual~A () {}Virtual voidFuna () {}}; classb:a{ Public: intb; B (): B (0xbbbbbbbb){} Virtual voidFunb () {}Virtual voidfunA2 () {}}; int_tmain (intARGC, _tchar*argv[]) {b b; return 0; }
A layout we already know, now B inherits a, and there is a virtual function covering a, look at the layout.
Obviously, in the case where the base class already has a virtual function table pointer, the derived class will no longer actively produce a virtual function table pointer, and the virtual function table pointer of the base class can be shared with the derived class, because the virtual function of the base class must also belong to the derived class. If a derived class has a virtual function that overrides the virtual function of the base class, the corresponding item inside the virtual function table is changed to the correct address, and the virtual function table pointer is placed exactly at the beginning of the class. So in this case, the base class is placed first and then the member variable is emitted. Let's take a look at what virtual function tables are now common to derived classes and base classes.
There are 4 items in the virtual pointer table, like the previous analysis method, we can draw a conclusion (note is the small end order) with the Disassembly window:
The virtual function table has 4 items:
1, the virtual function of the first item has been replaced by the funA2 in B, because B inside the funA2 has covered the base class A inside the funA2, so in the virtual function table also to change the corresponding, this is the virtual function is the premise of the correct call.
2, the second item, also replaced by the virtual destructor of B, we do not know in the code to write out the virtual destructor of B, the compiler will automatically generate a, and B's virtual destructor will overwrite the base class A virtual destructor.
3, the third item is also a function Funa, because in the derived class is not covered, so it should also be a function inside the base class.
4, the fourth item is the base class A does not have the function Funb, therefore in this common virtual function table inside the base class A only then uses the first 3 items, after the item is does not overwrite the base class the other virtual function, and is in the declaration order sequentially discharges.
So, for the time being, we can conclude that a class with virtual functions, in the case of single inheritance, produces a hidden member variable if the base class does not have a virtual function table pointer, a virtual function table pointer placed at the front of the class, then the base class, and finally the individual members of the derived class. If the base class already has a virtual function table pointer, it does not need to produce a virtual function table pointer, and the derived class can share a virtual function table with the base class, at which point the layout of the derived class is first put the base class and then the individual member variables of the derived class. If a derived class has a function that overrides a virtual function inside the base class, the corresponding item in the virtual function table is changed to the real address of the function, and the other virtual functions that are not covered are emitted in the order of the declarations in the subsequent items of the virtual function table.
3. Cases of multiple inheritance
Given that the first item of a class with a virtual function is a virtual function table pointer, it will be different from the normal case in the case of multiple inheritance. However, the object model with virtual function in the case of multiple inheritance is still relatively simple and definite.
There are probably two cases, one is that all base classes have no virtual functions, and one is a mixture of some virtual functions in the base class and no virtual functions.
In the first case, the memory layout is probably like this, for example, the base class of Class A has no virtual function.
class a:f0,f1,f2{intvirtual voidfun1 () {} ...};
Then a must also generate a virtual function table pointer, placed at the beginning of the position, in this case the equivalent model is probably this:
Class a{void * VF_PTR; f0{}; f1{}; f2{}; int A; (Other member variables) ...};
Note that each byte alignment is possible, especially for virtual function table pointers.
For the second case, the base class is a mixed case, such as Class A:
class int Virtual void fun1 () {} ...};
V0, V1, V2 are base classes with virtual functions, and F is a base class with no virtual functions, and the declaration order of inheritance is arbitrary. In this case, the object model of Class A is probably like this: first emit a base class with virtual functions in the base class, in order of declaration, and then emit base classes with no virtual functions in the base class, in order of declaration. For example, a at this point the object model is probably like this:
class a{v0{}; v1{}; v2{}; f0{}; f1{}; f2{}; int A; (Other member variables) ...};
Since the base class already has a virtual function table pointer, the derived class A can also be shared with the first base class with a virtual function table pointer to a virtual function table, which is the same as the single inheritance, the natural derived class will not generate a virtual function table pointer. Let's actually come up with an example of both cases.
3.1 base class has no virtual function
#include"stdafx.h" #pragmaPack (8)classf0{ Public:CharF0; F0 (): F0 (0xf0){} }; classf1{ Public:intF1; F1 (): F1 (0xf1f1f1f1){} }; classC:f1, f0{ Public: intC; Virtual voidFunC () {}Virtual voidFunb () {}Virtual voidfunA2 () {} c (): C (0x33333333){} }; int_tmain (intARGC, _tchar*argv[]) {c C; return 0; }
In cases where a derived class has a virtual function and the base class has no virtual function, the derived class will still produce a virtual function table pointer at the beginning, then to each base class, and finally to the member variable. Combine the disassembly window to see what is inside the virtual function table.
Virtual function table pointers have a total of 3 items, like the previous analysis method, we combine the Disassembly window, we can draw a conclusion (note is the small end order):
You can see that because the virtual function of the derived class does not overwrite the virtual function inside any base class, the items inside the virtual function table are the addresses of each virtual function in the order of the Declaration. Then look at the case where the base class has virtual functions and the derived class has a virtual function that overrides the base class.
3.2 Virtual functions in a base class
#include"stdafx.h" #pragmaPack (8)classf0{ Public:CharF0; F0 (): F0 (0xf0){} }; classf1{ Public:intF1; F1 (): F1 (0xf1f1f1f1){} }; classA { Public: intADoubleA2; A (): A (0xaaaaaaaa), A2 (0){} Virtual voidfunA2 () {}Virtual~A () {}Virtual voidFuna () {}}; classb:a{ Public: intb; B (): B (0xbbbbbbbb){} Virtual voidFunb () {}Virtual voidfunA2 () {}}; classc:f1, a,f0, b{ Public: intC; Virtual voidFunC () {}Virtual voidFunb () {}Virtual voidfunA2 () {} c (): C (0x33333333){} }; int_tmain (intARGC, _tchar*argv[]) {c C; return 0; }
The model for Class C is probably this:
class c{ public: A; b b; F1 F1; F0 F0; int c; };
It is clear that although the F1 declaration is at the top of the base class but the order is stored in the base class A virtual function first and then to the virtual function of the base class B, and then the individual no virtual function base class F1, F0. The last is the member variable of the derived class C. The virtual function of C Funb covers the virtual function inside the base class B, and the other virtual function funA2 covers both the virtual function inside the base class A and the virtual function funA2 inherited from the base class A from the base class B. In theory, the virtual functions that are covered in the base class A and the base class B are changed to the correct function address, that is, the real address of the virtual function inside the virtual function table. Then we look at what the virtual function table of a and B looks like.
Table of virtual functions shared by A and C:
Virtual function table pointers have a total of 4 items, like the previous analysis method, we combine the Disassembly window, we can draw a conclusion (note is the small end order):
virtual function table for B:
Virtual function table pointers have a total of 4 items, like the previous analysis method, we combine the Disassembly window, we can draw a conclusion (note is the small end order):
You can see that the various items in the virtual function table shared by the derived class and base class A have been modified to the actual address of the function, and a virtual function address entry that does not overwrite any of the base class virtual functions is added at the end. and the base class B inside the item is a bit unexpected, it is not directly modified to jump to the correct address up, but use an adjustment block of things, the EAX register minus the corresponding value, and then jump to the correct function inside, this is not here to repeat, Anyway, at the end of the jump to the C inside the function inside to just. The rest of the items have to be overwritten and are also modified to the correct function address.
******************************************************************************
Virtual voidfunA2 () {}Virtual~A () {}Virtual voidFuna () {}Virtual voidFunb () {}Virtual voidfunA2 () {}Virtual voidFunC () {}Virtual voidFunb () {}Virtual voidfunA2 () {} C::FUNA2C::~CA:funAc:func As to why there is no c:funb above, because there is no Class A in class B, all Class C does not need to be replaced,
Instead, for funa2,a,b,c three classes, we prefer the Class A c:funa2c:~CA:funAC:funB
memory allocation for C + + virtual functions