The previous section describes the encapsulation model in memory. The next section describes inheritance and polymorphism.
Ii. Memory layout with inheritance and Polymorphism
The memory layout and construction process under inheritance are described in detail in many books, so I will not elaborate here. Focuses on polymorphism.
Inheritance involves the following situations:
1. Single inheritance
2. Multi-Inheritance
3. Repeated inheritance
4. Virtual inheritance
1. Single inheritance
Assuming the following inheritance relationships exist, the general memory layout is as follows:
Code
Child: GrandChild:
Object layout:
The layout of member variables is well understood. What about the virtual function table when there are virtual functions?
To solve this problem, I improved the above Code.
fun1(){ cout<<<< fun2(){ cout<<<< fun3(){ cout<<<< Child: fun1() { cout<<<< c_fun2(){ cout<<<< c_fun3(){ cout<<<< GrandChild: fun1() { cout<<<< c_fun2(){ cout<<<< gc_fun3(){ cout<<<<
Use the debug window to view the virtual function table.
The three tables are different. The fun1 function is rewritten twice.
Which of the following is a pain point?GrandcYou can only see three functions. You have to do it yourself.
Following the content in the previous article, we use the pf function pointer:
(* PF )();
PF pf = NULL;
Write down the code in the main function:
* vtab = (*)*(*)& (; *vtab != NULL; vtab++= (PF)** member = (*)&<<*++member<<<<*++member<<<<*++member<<endl;
The output result of member variables is consistent with the conclusion in the previous article. Let's take a look at the virtual function section.
The first three functions are displayed in the same debugging window.
Based on the above results, we can draw the following conclusions:
1. During a single inheritance, different classes maintain different virtual function tables (only one), and the virtual function tables initially look like parent classes.
2. When overwrite occurs, for example, fun1 and c_fun2 will erase the virtual functions of the parent class, instead.
3. If no overwrite occurs, add it to the virtual function table.
Figure:
By now, combining the content of the previous article, we can easily understand why using the parent class pointer can produce polymorphism.
2. Multi-inheritance scenarios
Assuming the following inheritance relationships exist, the general memory layout is as follows:
Because it is multi-inheritance, according to 1, when a single inheritance class maintains a virtual function table. What should I do when there are too many inheritance?
It can only inherit several classes, and there are several virtual function tables.
The instance code is as follows:
fun1(){ cout<<<< fun2(){ cout<<<< fun3(){ cout<<<< fun1(){ cout<<<< fun2(){ cout<<<< fun3(){ cout<<<< fun1(){ cout<<<< fun2(){ cout<<<< fun3(){ cout<<<< Derived: Base1, Base2, fun1(){ cout<<<< d_fun(){ cout<<<<
Check the virtual function table in the debugging window:
You can clearly see the virtual function table of the base class from which the for base is labeled.
In addition, we can see that fun1 is overwritten in all three tables, so what we care aboutD_funWhich table will it be stored in?
We use the same method:
(* PF )();
PF pf = NULL;
* vtab1 = (*)*(*)& (; *vtab1 != NULL; vtab1++= (PF)** member1 = (*)&<<*++member1<< * vtab2 = (*)*((*)&dd + (Base1)/ (; *vtab2 != NULL; vtab2++= (PF)** member2 = (*)((*)&dd + (Base1)/<<*++member2<< * vtab3 = (*)*((*)&dd + ((Base1)+(Base2))/ (; *vtab3 != NULL; vtab3++= (PF)** member3 = (*)((*)&dd + ((Base1)+(Base2))/<<*++member3<<endl;
It is a bit lazy. Because int type is used, there is no byte alignment. sizeof/4 is used directly, and this offset is used to access different base regions.
The output result is as follows:
We can see that d_fun is placed in the first function table (the first in the Declaration Order, and the instance code is part of base1 ).
Conclusion:
1. In case of multi-inheritance, during overwirte, all the functions of the parent class will be overwritten in three tables.
2. The new virtual function added by the subclass is placed in the first virtual function table.
Figure:
3. Repeated inheritance
In fact, repeated inheritance is only a special case of multi-inheritance. All rules are still implemented according to the rules of Multi-inheritance. In particular, the grandfather class generates two copy images to duplicate the data and create ambiguity.
From the perspective of design or maintenance, this is a failure.
Therefore, we will skip to virtual inheritance without focusing on it.
4. scenarios of virtual inheritance
There are actually many ways to use the object model of virtual inheritance. The environment used in this article is vs2008, Which is Microsoft's trick. The in-depth C ++ Object Model Book clearly states
In the case of virtual inheritance, there is no fixed standard for the construction of object models. The main idea is to split themUnchanged local and shared local. Of course, only better methods are used to achieve higher access efficiency.
Therefore, the memory layout described in this article may only be used in Microsoft compilers. Because of this, we focus on the effects of virtual inheritance.
Assume that the following inheritance relationships exist:
Instance code:
fun(){ cout<<<< B_fun(){ cout<<<< Base1: fun(){ cout<<<< fun1(){ cout<<<< B_fun1(){ cout<<<< Base2: fun(){ cout<<<< fun2(){ cout<<<< B_fun2(){ cout<<<< Derived: Base1, fun(){ cout<<<< fun1(){ cout<<<< fun2(){ cout<<<< D_fun(){ cout<<<<
Let's take a look at the situation of single virtual inheritance.Base1Layout:
Bb is the Base object, and bb1 is the Base1 object.
Obviously, we can see that different from a single inheritance, we use two virtual function pointers, a table pointing to the Base class of the virtual Base, and we generate a table ourselves.
The virtual function fun pointing to the table of the virtual Base class is obviously overwritten.
Read using code:
* vtab = (*)*(*)& (; *vtab != NULL; vtab++= (PF)*
This loop is interrupted because vtab accesses a magic number-4, which is used to separate and accidentally access it. (Mr Chen Hao's blog titled memory layout of C ++ objects also encountered the same problem, but GCC didn't)
It is sufficient to prove that the unchanged part here is the function added by Derived itself later. The shared local fun goes to the virtual function table contained in the virtual base class.
We use the second-level pointer to solve the problem of interruption.
** pVtab = (**)& pf = (PF)pVtab[][= (PF)pVtab[][ cout << pVtab[][] << endl;<< ()*((*)(&bb1)+) <<endl; <<<<(*)*((*)(&bb1)+) <<endl; pf = (PF)pVtab[][= (PF)pVtab[][<< pVtab[][] << endl;<< ()*((*)(&bb1)+) <<endl;
We can see the memory layout:
1. The unchanged layout (subclass) is placed at the front of the object model, and the shared layout (Virtual base class) At the end.
2. In the subclass part, the virtual function table uses-4 as the separator end. Next is the variable value of the subclass member.
3.Virtual base classA shared part is a normal virtual function table layout, and the fun function is rewritten.
Figure:
This ensures that the shared part is in the virtual base class (including the virtual function table), and the unchanged part is in the subclass.
Next, let's look at the complete inheritance structure and parse it.Derived.
Code:
** pVtab = (**)& pf = (PF)pVtab[][= (PF)pVtab[][<< pVtab[][] << endl;<< ()*((*)(&dd)+) <<endl; pf = (PF)pVtab[][= (PF)pVtab[][<< pVtab[][] << endl; cout << ()*((*)(&dd)+) <<endl; cout << ()*((*)(&dd)+) <<endl; cout << <<(*)*((*)(&dd)+) <<= (PF)pVtab[][= (PF)pVtab[][<< ()*((*)(&dd)+) <<endl;
Running result:
Similar to a single virtual inheritance:
1. According to the declared order, the unchanged layout (parent class) is placed at the front end of the Object Model in sequence, and the shared layout (virtual base class) is placed at the tail end.
2. The layout is unchanged. The virtual function table uses-4 as the separator end. Next is the variable value of the subclass member.
3. The virtual base class belongs to the shared part. It is a normal virtual function table layout and overwrites the fun function.
The figure is not shown, which is similar to a single virtual inheritance.
The following is a reference to the book deep into the C ++ object model:
It is difficult to support virtual inheritance in the compiler.
The difficulty lies in finding a valid method to fold the Base portion maintained by Base1 and Base2 into a Base portion maintained by a single Derived, it can also maintain the polymorphism between the pointer of base class and Derived class.
This is also the result of virtual inheritance.
So far, the whole article is almost finished.
For more information, see the in-depth C ++ Object Model Book and the blog post of Mr Chen Hao mentioned above. The content is a little longer and may be missed. I hope you can correct it.