I was reading this articles , was at this heading Inheritance of Base-class vPtrs , but couldn\'t understand what did he mean in this para :
"However, due to mul
With regards to your first question, I don't really follow what the quoted passage is getting at; it actually sounds like the author doesn't really understand how vtables work (or hasn't thought about it in detail). It's important to realize that when we speak of “merging” the base and the derived classes' vtables, we are talking about making the base class vtable a prefix of the derived class table; the base class vtable must start at the start of the derived class vtable for this to work; the offset of the vptr in both the base and the derived must be identical (almost always 0, in practice), and the base class must be placed at the very beginning of the derived. And of course, it's only possible to fulfil those conditions for one base class. (Most compilers will use the first non-virtual base appearing in a left to right scan of the code.)
With regards to the expression, it's completely undefined behavior, and
will not work with some compilers. Or may or may not work, depending on
the level of optimization. And the void*
in it is being used as a
placeholder for any number of pointer types (including, probably,
pointer to function types). If we take the inner-most part, we're
saying that &a
is a pointer to (1 or more) void*
. This pointer is
then dereferenced ((X)[0]
is the same as *(X)
, so
(((void**)(&a))[0])
is the same as *(void**)(&a)
. (The [0]
notation suggests that there might be more values behind this one; i.e.
that [1]
, etc. might also be valid. That's not the case here.) This
results in a void*
, which is then cast to a void**
and once again
dereferenced, this time really using an index (since it is hopefully
into an array); the result of this dereferencing is converted to a foo
(a pointer to a function), which is then dereferenced and the function
is called without any arguments.
None of this will actually work. It makes a number of suppositions which are not always, or in some cases even generally, true:
void*
. (This is almost always true, and required by Posix.)void*
. And while
it's true that pointers to functions do often have the same size as
void*
(again, Posix requires it, and it's also true under Windows),
it's hard to imagine an implementation which would work if the vtable
were just an array of pointers to functions.He apparently is using VC++ (based on the __thiscall
, which is a
Microsoft'ism), and I've only analysed the layout of Sun CC, which is
definitely different. (And Sun CC and g++ are also very
different—for that matter, Sun CC 3.1, Sun CC 4.0 and Sun CC 5.0
are all different.)
Unless you're actually writing a compiler, I'd ignore all of this. And I'd certainly ignore the expression you quote.
If Derived inherits from Base, its vtable will extend Base's. Now, if it also inherits Base2, its vtable will not include Base2's - Base2's part will retain its vtable (updated with Derived virtual functions, if they override Base2's).
Base members Base2 members Derived members
+--+------------+----+------------+------------------+
| |
V V
Derived + Base Base2 vtable
vtable
To make the second question easier to understand, and since I enjoy drawing with fixed width font... here's the memory layout of a
. For a full explanation about that expression, see James Kanze's answer.
+---+----------+
a: | | | A |
+-+-+----------+
|
V
vtable: +---+
| --+--> f2()
+---+
| --+--> f3()
+---+
HTH...
Virtual function calls are commonly implemented using a virtual method table. Compilers typically create one such table for each class in your program, and give each object a pointer to the virtual table that corresponds to its class when the object is instantiated (this pointer is referred to as vptr
). This way, the object "knows" exactly which function to call when you call a virtual method regardless of its static type.
As I mentioned above, normally each class gets its own virtual method table. The paragraph you cite says that if a class derives from e.g. 5 base classes, each of which also derives from 5 other classes, then the virtual table for this class should end up being a merged version of all 25 virtual tables of the base classes. This would be kind of wasteful, so the compiler might decide to only merge the 5 virtual tables from the "immediate" base classes into the derived class's virtual table, and keep the vptr
s to the other 20 virtual tables stored as a hidden member inside the class (which would now have 21 vptr
s total).
The benefit in doing this is that you don't need to reserve memory repeating the same information every time a class with a virtual table is derived from. The downside is that you complicate the implementation (e.g. when calling a virtual method, the compiler now has to somehow figure out which one of the vptr
s points to the table that tells it which method to call).
Regarding the second question, I 'm not sure what exactly you are asking. This code assumes that the vptr
is the very first item in the memory layout of object of that class (which is practically true often, but a horrible hack since nowhere does it say that virtual methods are even implemented using a virtual table; there might not even be a vptr
), fetches the second item from the table (which is a pointer to a member function of the class) and runs it.
Expect fireworks if even the slightest thing goes wrong (e.g.: there was no vptr
, the vptr
's structure is not what the person who wrote the code expected, a later version of the compiler decides to change how it stores the virtual table, the method pointed to has a different signature, etc etc etc).
Update (addressing the comments):
Let's say we have
class Child : Mom, Dad {};
class Mom : GrandMom1, GrandDad1 {};
class Dad : GrandMom2, GrandDad2 {};
In this case, Mom
and Dad
are the immediate base classes ("first-born", but the term is misleading).
for question 1: i think the paragraph is just saying that the vtables aren't actually merged into one and stored in the memory allocation of the class that is multiply deriving; but that the vtables of the base classes beyond the first base classes are used by reference.
in other words; if you have Rose which derives from Flower which derives from Plant then Rose only incorporates Flower's vtable directly, but uses of Plant's vtable are done by calling them from Plant's vtable. )
for question 2: i am not so great at doing these in my head, i would have to just start breaking it down into manageable chunks to understand.
first i would tabify it like so:
(
*(foo)
(
(void**)
(
((void**)(&a))[0]
)
)
[1]
)
();
then,
Step 1:
((void**)(&a))[0]
we know (X)[0]
= *X
let X
= (void**)&a
X[0]
= ((void**)&a)[0]
= (void*)a
now replace:
(
*(foo)
(
(void**)
(
(void*)(a)
)
)
[1]
)
();
Step 2:
(void**)((void*)(a))
= (void**)(void*)a = (void**)a
(
*(foo)
(
(void**)a
)
[1]
)
();
Step 3:
so it looks like we are left with a function pointer
(foo)((void**)a)[1]
= (foo)((void*)(a+1))
or, what looks like a void* to function at position (a+1), of type foo...
i think that's at least close to right :) function pointers always give me problems. ;)