2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 1/10
Home | Download PHC | Documentation | Developers and Contributors | Mailing List |
Memory Layout for multiple and Virtual inheritance
(by Edsko de Vries, January 2006)
Warning. This article is rather technical and assumes a good knowledge of C + + and some assembly language.
In this article we explain the object layout implemented to GCC for multiple and virtual inheritance.
Although in a ideal world C + + programmers should not need to know these details of the compiler internals,
Unfortunately the multiple (and especially virtual) inheritance is implemented have various non-obvious
Consequences for writing C + + code (in particular, for downcasting pointers, using pointers to pointers, and
The invocation order of constructors for virtual bases). If you understand how multiple inheritance is
Implemented, you'll be able anticipate these consequences and deal with them in your code. Also, it is
Useful to understand the cost of using virtual inheritance if your care is about efficiency. Finally, it is
Interesting:-)
Multiple inheritance
First we consider the relatively simple case of (non-virtual) multiple inheritance. Consider the following
C + + class hierarchy.
Class Top
{p
Ublic:
int A;
};
Class Left:public Top
{p
Ublic:
int b;
};
Class Right:public Top
{p
Ublic:
int C;
};
Class Bottom:public left, public right
{p
Ublic:
int D;
};
Using a UML diagram, we can represent this hierarchy as
2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 2/10
Note that the Top is inherited from twice (this is known as repeated inheritance in Eiffel). This means
Object bottom of type bottom would have both attributes called a (accessed as bottom. Left::a and
Bottom. RIGHT::A).
How is left, right and Bottom laid out in memory? We Show the simplest case first. Left and right has the
Following structure:
Left
Top::a
Left::b
Right
Top::a
Right::c
Note that the first attribute are the attribute inherited from Top. This means. After the following
Assignments
Left* left = New Left ();
top* top = left;
Left and top can point to the exact same address, and we can treat the left object as if it were a top object
(and obviously a similar thing happens for right). What's about Bottom? GCC suggests
Bottom
Left::top::a
Left::b
Right::top::a
Right::c
Bottom::d
Now what happens when we upcast a Bottom pointer?
bottom* Bottom = new Bottom ();
left* left = bottom;
This works out nicely. Because of the memory layout, we can treat an object of type Bottom as if it were an
Object of type left, because the memory layout of both classes coincide. However, what happens when we upcast
to right?
right* right = bottom;
For the-to-work, we had to adjust the pointer value-to-make it point-to-the-corresponding section of the
Bottom layout:
Bottom
Left::top::a
Left::b
Right right::top::a
2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 3/10
Right::c
Bottom::d
After this adjustment, we can access bottom through the right pointer as a normal right object; However,
Bottom and right now point to different memory locations. For completeness ' sake, consider what would happen
When we do
top* top = bottom;
Right. This statement is Ambiguous:the compiler would complain
Error: ' Top ' is an ambiguous base of ' Bottom '
The possibilities can disambiguated using
top* TOPL = (left*) bottom;
top* TOPR = (right*) bottom;
After these-assignments, TOPL and left would point to the same address, as would topr and right.
Virtual inheritance
To avoid the repeated inheritance of top, we must inherit virtually from top:
Class Top
{p
Ublic:
int A;
};
Class Left:virtual Public Top
{p
Ublic:
int b;
};
Class Right:virtual Public Top
{p
Ublic:
int C;
};
Class Bottom:public left, public right
{p
Ublic:
int D;
};
This yields the following hierarchy (which are perhaps what you expected in the first place)
2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 4/10
While this may seem more obvious and simpler from a programmer's point of view, from the compiler's point of
View, this is vastly more complicated. Consider the layout of Bottom again. One (non) possibility is
Bottom
Left::top::a
Left::b
Right::c
Bottom::d
The advantage of this layout is, the first part of the layout collides with the layout of the left, and we
Can thus access a Bottom easily through a left pointer. However, what is we going to does with
right* right = bottom;
Which address do we assign? After this assignment, we should is able to use right as if it were
Pointing to a regular right object. However, this is impossible! The memory layout of right itself is
Completely different, and we can thus no longer access a "real" right object in the same-as an upcasted
Bottom object. Moreover, no other (simple) layout for Bottom would work.
The solution is non-trivial. We'll show the solution first and then explain it.
You should note the things in this diagram. First, the order of the fields are completely different (in fact,
It is approximately the reverse). Second, there is these new vptr pointers. These attributes is
automatically inserted by the compiler when necessary (if using virtual inheritance, or when using virtual
Functions). The compiler also inserts code into the constructor to initialise these pointers.
The Vptrs (virtual pointers) index a "virtual table". There is a vptr for every virtual base of the class.
To see how the virtual table (vtable) is used, consider the following C + + code.
bottom* Bottom = new Bottom ();
left* left = bottom;
int p = left->a;
The second assignment makes to the same address as bottom (i.e., it points to the "top" of the
Bottom object). We consider the compilation of the last assignment (slightly simplified):
Movl left,%eax #%eax = Left
MOVL (%eax),%eax #%eax = Left.vptr.Left
MOVL (%eax),%eax #%eax = Virtual base offset
Addl left,%eax #%eax = left + Virtual base offset
MOVL (%eax),%eax #%eax = left.a
Movl%eax, p # p = left.a
In words, we use left to index the virtual table and obtain the "virtual base offset" (vbase). This offset
Was then added to left, which was then used to index the TOP section of the Bottom object. From the diagram,
You can see that the virtual base of offset for left is 20; If you assume this all the fields in Bottom is 4
Bytes, you'll see this adding bytes to left would indeed point to the a field.
With this setup, we can access the right part of the same. After
bottom* Bottom = new Bottom ();
right* right = bottom;
int p = right->a;
Right would point to the appropriate part of the Bottom object:
Bottom
2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 5/10
Vptr. Left
Left::b
Right vptr. Right
Right::c
Bottom::d
Top::a
The assignment to P can now is compiled in the exact same as we do previously for left. The only
Difference is, the vptr we access now points to a different part of the virtual table:the virtual base
Offset we obtain is a, which is correct (verify!). We can summarise this visually:
Of course, the point of the exercise is to is able to access real right objects the same as upcasted
Bottom objects. So, we had to introduce vptrs in the layout of right (and left) too:
Now we can access a Bottom object through a right pointer without further difficulty. However, this has come
At rather large expense:we needed to introduce virtual tables, classes needed to being extended with one or
More virtual pointers, and a simple attribute lookup in an object now needs-indirections through the
Virtual table (although compiler optimizations can reduce that cost somewhat).
Downcasting
As we have seen, casting a pointer of type derivedclass to a pointer of type superclass (in other words,
upcasting) may involve adding a offset to the pointer. One might is tempted to think that downcasting (going
The other is the can then simply is implemented by subtracting the same offset. And indeed, the case
For non-virtual inheritance. However, virtual Inheritance (unsurprisingly!) introduces another complication.
Suppose we extend our inheritance hierarchy with the following class.
Class Anotherbottom:public left, public right
{p
Ublic:
int e;
int F;
};
The hierarchy now looks like
2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 6/10
Now consider the following code.
bottom* bottom1 = new Bottom ();
anotherbottom* bottom2 = new Anotherbottom ();
top* top1 = bottom1;
top* top2 = bottom2;
Left* left = static_cast<left*> (TOP1);
The following diagram shows the layout of Bottom and Anotherbottom, and shows where top is pointing after the
Last assignment.
Bottom
Vptr. Left
Left::b
Vptr. Right
Right::c
Bottom::d
Top1 top::a
Anotherbottom
Vptr. Left
Left::b
Vptr. Right
Right::c
Anotherbottom::e
Anotherbottom::f
TOP2 top::a
Now consider how to implement the static cast from Top1 to left and while taking into account so we do not
Know whether TOP1 is pointing to a object of type Bottom or an object of type Anotherbottom. It can ' t be
done! The necessary offset depends on the runtime type of TOP1 (for Bottom and Anotherbottom). The
Compiler would complain:
Error:cannot convert from base ' Top ' to derived type ' left '
Via virtual base ' Top '
Since We need runtime information, we need to use a dynamic cast instead:
Left* left = dynamic_cast<left*> (TOP1);
However, the compiler is still unhappy:
Error:cannot dynamic_cast ' top ' (of type ' class top* ') to type
' Class left* ' (source type is not polymorphic)
The problem is, a dynamic cast (as well as use of typeid), needs runtime type information about the object
pointed to by TOP1. However, if you are in the diagram, you'll see the
pointed to by TOP1 are an integer (a). The compiler did not include a vptr. Top because it did not think that
was necessary. To force the compiler to include this vptr, we can add a virtual destructor to Top:
Class Top
{p
Ublic:
2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 7/10
Virtual ~top () {}
int A;
};
This change necessitates a vptr for Top. The new layout for Bottom
(Of course, the other classes get a similar new vptr. Top attribute). The compiler now inserts a library call
For the dynamic cast:
left = __dynamic_cast (Top1, Typeinfo_for_top, Typeinfo_for_left,-1);
This function __dynamic_cast was defined in libstdc++ (the corresponding header, file is cxxabi.h); Armed with
The type information for Top, left and Bottom (through Vptr. TOP), the cast can be executed. (The-1 parameter
Indicates the relationship between left and Top are presently unknown). For details, refer to the
Implementation in tinfo.cc.
Concluding Remarks
Finally, we tie a couple of loose ends.
(in) variance of Double pointers
This is were it gets slightly confusing, although it's rather obvious when you give it some thought. We
Consider an example. Assume the class hierarchy presented in the last section (downcasting). We have seen
Previously what's the effect is
bottom* B = new Bottom ();
right* r = b;
(The value of B gets adjusted by 8 bytes before it's assigned to R
of the Bottom object). Thus, we can legally assign a bottom* to a right*. What is about bottom** and right**?
bottom** BB = &b;
right** rr = BB;
Should the compiler accept this? A Quick test would show that the compiler would complain:
Error:invalid conversion from ' bottom** ' to ' right** '
Why? Suppose the compiler would accept the assignment of BB to RR. We can visualise the result as:
So, BB and RR both point to B, and B and R point to the appropriate sections of the Bottom object. Now
2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 8/10
Consider what happens if we assign to *RR (note that the type of *RR are right*, so this assignment is
Valid):
*RR = b;
This was essentially the same assignment as the assignment to r above. Thus, the compiler would implement it
The same way! In particular, it would adjust the value of B by 8 bytes before it assigns it to *RR. But *RR
Pointed to b! If we visualise the result again:
Correct as long as we access the Bottom object through *RR, but as soon as we access it through B
itself, all memory references is off by 8 bytes-obviously a very undesirable situation.
So, in summary, even if *a and *b is related by some subtyping relation, **a and **b is not.
Constructors of Virtual Bases
The compiler must guarantee, all virtual pointers of an object is properly initialised. In particular,
It guarantees the constructor for all virtual bases of a class get invoked, and get invoked only once.
If you don ' t explicitly call the constructors of your virtual superclasses (independent of
Tree they is), the compiler would automatically insert a call to their default constructors.
This can leads to some unexpected results. Consider the same class hierarchy again we have been considering so
Far, extended with constructors:
Class Top
{p
Ublic:
Top () {a =-1;}
Top (int _a) {a = _a;}
int A;
};
Class Left:public Top
{p
Ublic:
Left () {b =-2;}
Left (int _a, int _b): Top (_a) {b = _b;}
int b;
};
Class Right:public Top
{p
Ublic:
Right () {c =-3;}
Right (int _a, int _c): Top (_a) {c = _c;}
int C;
};
Class Bottom:public left, public right
{p
Ublic:
Bottom () {d =-4;}
Bottom (int _a, int _b, int _c, int _d): Left (_a, _b), right (_a, _c)
{
D = _d;
}
int D;
};
2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 9/10
(We Consider the non-virtual case first.) What would your expect this to output:
Bottom Bottom (1,2,3,4);
printf ("%d%d%d%d\n", bottom. Left::a, bottom. Right::a,
Bottom.b, BOTTOM.C, BOTTOM.D);
You would probably expect (and get)
1 1 2) 3 4
However, now consider the virtual case (where we inherit virtually from Top). If we make this single change,
and run the program again, we instead get
-1-1 2 3 4
Why? If you trace the execution of the constructors, you'll find
Top::top ()
Left::left (ON)
Right::right (1,3)
Bottom::bottom (1,2,3,4)
As explained above, the compiler have inserted a call to the default constructor in Bottom, before the
Execution of the other constructors. Then when the left tries-to-call it Superconstructor (TOP), we find that
Top have already been initialised and the constructor does not get invoked.
To avoid this situation, you should explicitly call the constructor of your virtual base (s):
Bottom (int _a, int _b, int _c, int _d): Top (_a), left (_a,_b), right (_a,_c)
{
D = _d;
}
Pointer equivalence
Once again assuming the same (virtual) class hierarchy, would do expect this to print "Equal"?
bottom* B = new Bottom ();
right* r = b;
if (r = = b)
printf ("equal!\n");
Bear on Mind, the addresses is not actually equal (R was off by 8 bytes). However, that's should be
Completely transparent to the user; So, the compiler actually subtracts the 8 bytes from R before comparing
it to B; Thus, the addresses is considered equal.
Casting to void*
Finally, we consider what happens we can cast a object to void*. The compiler must guarantee that a pointer
Cast to void* points to the "top" of the object. Using The vtable, this was actually very easy to implement.
Been wondering what's the offset to top field is. It is the offset from the vptr to the top of the
Object. So, a cast to void* can is implemented using a single lookup in the vtable. Make sure to use a
Dynamic cast, however, thus:
Dynamic_cast<void*> (b);
References
[1] codesourcery, in particular the C + + Abi Summary, the Itanium C + + ABI (despite the name, this document is
Referenced in a platform-independent context; In particular, the structure of the vtables are detailed here).
The libstdc++ implementation of dynamic casts, as well RTTI and name unmangling/demangling, was defined in
tinfo.cc.
[2] The libstdc++ website, in particular the sections on the C + + standard Library API.
[3] C + +: Under the Hood by Jan Gray.
[4] Chapter 9, "multiple inheritance" of thinking in C + + (Volume 2) by Bruce Eckel. The author has made
2016/6/7 PHC Memory Layout for multiple and Virtual inheritance Edsko de Vries
Http://www.phpcompiler.org/articles/virtualinheritance.html 10/10
This book available for download.
$LastChangedDate: 2009-05-31 14:03:14 +0100 (Sun, May 2009) $.
Memory Layout for multiple and Virtual inheritance