Applications written in the past few days are inherited from multiple applications.
In the past, the concept of multi-inheritance was very clear, but it was a bit vague if it was useless for a long time. Study again and "refresh" the memory.
Suppose we have the following code:
# Include <stdio. h>
Class
{
PRIVATE:
Char data;
Public:
A () {DATA = 'a ';}
Virtual void show () {printf ("A \ n ");};
Virtual void dispa () {printf ("A \ n ");};
};
Class B
{
PRIVATE:
Int data;
Public:
B () {DATA = 'B ';}
Virtual void show () {printf ("B \ n ");};
Virtual void dispb () {printf ("B \ n ");};
};
Class C
{
PRIVATE:
Char data;
Public:
C () {DATA = 'C ';}
Virtual void show () {printf ("C \ n ");};
Virtual void dispc () {printf ("C \ n ");};
};
Class D: Public A, public B, public C
{
Public:
Char data;
Public:
D () {DATA = 'D ';}
Virtual void show () {printf ("d \ n ");};
Virtual void dispd () {printf ("d \ n ");};
};
Class E: Public d
{
PRIVATE:
Char data;
Public:
E () {DATA = 'E ';}
Virtual void show () {printf ("e \ n ");};
Virtual void dispe () {printf ("e \ n ");};
};
Int main ()
{
D * D = new D;
A * A = (A *) D;
B * B = (B *) D;
C * c = (C *) D ;;
D-> show ();
A-> show ();
B-> show ();
A-> dispa ();
B-> dispb ();
D-> dispd ();
D * D1 = (D *);
D1-> show ();
D1-> dispd ();
D * D2 = (D *) B;
D2-> show ();
D2-> dispd ();
Char x = D-> data;
Return 0;
}
Each class has two virtual functions: Show () and dispx (). Classes A, B, and C are basic classes, while D is multi-inheritance. Finally, e inherits D. So what is the memory image of Class E? To answer this question, let's review the memory image of the basic class:
+ -------------- + <-This
+ Vtab +
+ -------------- +
++
+ Data +
++
+ -------------- +
If a class has a virtual function, it has a virtual function table (vtab ). The first unit of the class is a pointer pointing to the virtual function table. If the class does not have a virtual function and its ancestor (all parent classes) does not have a virtual function, its memory image is of the same structure as that of C. A virtual function table is an array. Each unit points to a virtual function address.
If class Y is an inheritance of Class X, the memory image of class Y is as follows:
+ -------------- + <-This
+ Y vtab +
+ -------------- +
++
+ X Data +
++
+ -------------- +
++
+ Y Data +
++
+ -------------- +
The virtual function table of Y is similar to that of X. If y has a new virtual function, add one at the end of the vtab. If y re-defines the original virtual function, the original Pointer Points to the new function entry. In this way, both memory impressions and virtual function tables are compatible with X. In this way, after running X * x = (y *) y;, X can be used well and enjoy new virtual functions.
Now we can see multi-inheritance:
Class D: Public A, public B, public C
{
....
}
Its memory image is as follows:
+ -- + ----------------- + 00 H <-this
++ D vtab +
+ A + ----------------- + 04 H
++ A's data +
+ -- + ----------------- + 08 h
+ + B's vtab '+
+ B + ----------------- + 0ch
++ B's data +
+ -- + ----------------- + 10 h
++ C's vtab '+
+ C + ----------------- + 14 h
++ C data ++
+ -- + ----------------- + 18 h
+ D Data +
+ -- + ----------------- +
(Because it is aligned with the double-character, ~ D Data is only a char, but must be aligned to DWORD, so it occupies 4 bytes)
For A, it is no different from single inheritance. B and C are simply placed behind. If their virtual functions have been redefined in D (such as the show function), they need to use the new vtab, place the redefined virtual function in the correct position (this is critical to com or similar technologies ). Finally, D's data is placed at the end.
E memory image problems can be self-evident.
Next, let's take a look at how C ++ works.
D * D;
......
B * B = (B *) D;
Such requirements. Set breakpoints and go to disassembly. You can see the following Assembly Code: (because of the UBB relationship, square brackets are replaced with braces. Looks awkward)
B * B = (B *) D;
00401062 cmp dword ptr {d}, 0
00401066 je main + 73 h (401073 H)
00401068 mov eax, dword ptr {d}
0040106bAdd eax, 8
0040da-e mov dword ptr {ebp-38h}, eax
00401071 JMP main + 7ah (401_ah)
00401073 mov dword ptr {ebp-38h}, 0
0040107a mov ECx, Dwords PTR {ebp-38h}
0040107d mov dword ptr {B}, ECx
From the assembly code above, we can see that if the source (d Here) is null, the target (B Here) will also be set to null, otherwise, the target will point to the source address and offset 8 bytes downward, which is exactly the vtab position of B. As to why the ebp-38h is used as a cache, This is the compiler's algorithm problem. Wait for time to study again.
Next, let's look at a strange question. This is also the original intention of my article:
According to the above multi-inheritance definition, can we find the instance of d if instance B of Class B is given?
Why. The following possibilities exist:
Class B
{
...
Virtual int gettypeid () = 0;
...
};
Class D: Public A, public B, public C
{
...
Virtual int gettypeid () {return 0 ;};
...
};
Class Z: Public X, public y, public B
{
...
Virtual int gettypeid () {return 1 ;};
...
};
Void myfunc (B * B)
{
Int T = B-> gettypeid ();
Switch (t)
{
Case 0:
Dosomething (D *) B); // is it possible?
Break;
Case 1:
Dosomething (z *) B); // is it possible?
Break;
Default:
Break;
}
}
I think it is very doubtful. But if you think about it, it is possible and facts prove this. Because the compiler understands the relationship between the classes D and B (that is, the offset), it will convert accordingly. Similarly, set a breakpoint to view the Assembly:
D * D2 = (D *) B;
00419992 cmp dword ptr {B}, 0
00419996 je main + 196 H (4199a6h)
00419998 mov eax, dword ptr {B}
0041999bSub eax, 8
0041999e mov dword ptr {ebp-13Ch}, eax
004199a4 JMP main + 1a0h (4199b0h)
004199a6 mov dword ptr {ebp-13Ch}, 0
004199b0 mov ECx, dword ptr {ebp-13Ch}
004199b6 mov dword ptr {d2}, ECx
If the source (B Here) is null, the target (D2 here) is also null. Otherwise, the target will take the source address and offset it up to 8 bytes, which points to the instance location of D. Similarly, for the need for ebp-13Ch cache, to be checked.
Some time ago, there was a problem with converting an interface into a corresponding class in. net. Today, the review of the multi-inheritance of c ++ completely eliminates doubts.