About Me

My photo
I'm a colonist who has declared war on machines and intend to conquer them some day. You'll often find me deep in the trenches fighting off bugs and ugly defects in code. When I'm not tappity-tapping at my WMD (also, known as keyboard), you'll find me chatting with friends, reading comics or playing a PC game.

Tuesday, December 23, 2008

Casting with Multiple Inheritance

(Reader Level : Intermediate)
(Knowledge assumptions : C++ casts, Multiple Inheritance)


The Question:
Given the classes:
struct A { int _aValue; };

struct B { float _bValue; };

class C : public A, public B { };
Consider the following statements:
C objC;

void *pC = &objC;

A *pA = reinterpret_cast<A*>( pC );

B *pB = reinterpret_cast<B*>( pC );

...
Are both pA and pB valid? Why or why not?

The Answer:
pA is valid. pB is invalid.

The Reason:
Many feel that the order in which base classes are inherited does not matter for multiple inheritance. However, in some cases, it does matter. This is one of those cases.

When creating an instance of C, its A subobject will be instantiated first and then its B subobject. This behaviour is due to the fact that C inherits from A first and then from B. The 'this' pointer of the A subobject will be at an offset of 0 bytes from the 'this' pointer of objC. On the other hand, the B subobject will be at an offset of 4 bytes from the 'this' address of objC. The displacement of 4 bytes is attributed to the integer member _aValue of class A.
The following diagrams should make things clear.



Now, what happens when you cast from a void* to A*? The compiler doesn't know that the actual type of pC is C*. So, it assumes that pA can point to the 'this' address of pC. That's fair because the A subobject of an instance of C will be at the starting address of objC. However, when making the cast from void* to B*, the compiler assumes that the B subobject is at the starting address of objC. That isn't true and hence, pB is invalid.

Corollary:
Now that we know why pB is invalid, let's see if we can correct it. Obviously, reinterpret_cast is ugly and unreliable.
Would a static_cast do any better?
Unfortunately, no. A static_cast works at compile time and as stated before the compiler doesn't know anything about the actual type of pC.
How about a dynamic_cast?
A dynamic_cast would nip our futile attempts in the bud by refusing to cast from void* to anything.

The take-home message:
reinterpret_cast is bad but void* is evil!

1 comment:

Unknown said...

Thanks~!!! My problem solved with this post.