This question is very similar to this one Why can't I dynamic_cast "sideways" during multiple inheritence?, except that the cast does work - just not inside in the constructor.
Header:
class A
{
public:
virtual ~A() {}
void printA();
};
class B
{
public:
B();
virtual ~B() {}
void printB();
private:
std::string message_;
};
class C : public A, public B
{
public:
C() {}
virtual ~C() {}
};
Source:
void A::printA() { cout << "A" << endl; }
B::B()
{
A* a = dynamic_cast< A* >( this );
if ( a ) {
message_ = std::string( "A and B" );
} else {
message_ = std::string( "B" );
}
}
void B::printB() { cout << message_.c_str() << endl; }
Main:
int main( int argc, char* argv[] )
{
cout << "Printing C..." << endl;
C c;
c.printA();
c.printB();
cout << "Checking again..." << endl;
cout << !!dynamic_cast< A* >( &c ) << endl;
return EXIT_SUCCESS;
}
Result:
Printing C...
A
B
Checking again...
1
So, the dynamic_cast does work for multiple inheritance (no surprises there!), but why not when called at runtime for the 'this' pointer inside B::B()? I thought that the object was fully formed once inside the body of the constructor i.e. all the memory was allocated for the component objects, they haven't been initialised yet. I appreciate that this depends on the superclass constructor order, but in this example A is called before B.
I am obviously not understanding what exactly is happening under the hood, can someone please enlighten me?
Thanks, Cam Bamber.
Basically the standard says it will not work (dynamic_cast) during construction of an object. <quote>
Edit: Added based on VJo comment below.
Note: The cast from a 'B' to an 'A' using dynamic cast should work because we are casting an object of type 'C'. If we added the following code to main:
The extra output would be:
It fails in the constructor because the object is not fully formed. Using this we are trying to convert a C pointer into a B pointer before the C constructor has started (the code defined by the user). Thus the use of
this
in B::B() as a pointer to a C object fails thus when the dynamic_cast<> is called on this it fails to do what you want it to because of UB.12.7 Construction and destruction [class.cdtor]
Paragraph 3
[ Example:
— end example ]
</quote>
Each base class constructor is executed before the derived class constructor, and during the
B
constructor, the dynamic type of the object isB
; it does not become aC
until you enter theC
constructor. So you cannot do anything that requires a dynamic type ofC
: you can't cross-cast to any ofC
s other base classes, and if you called a virtual function, then you would not get any overrides provided byC
.Under the hood, the dynamic type is (in most implementations at least) determined by a pointer in the object (known as the "vptr"), which points to some static data specifying properties of the class, including a table of virtual functions (known as the "vtable") and the information needed for
dynamic_cast
andtypeid
. Before each constructor, this is updated to point to the information for the class currently under construction.Since
B
doesn't inherit fromA
(B
is parent-most class), the dynamic type ofB
during its constructor isB
. Only when both theA
andB
parents are constructed can the childC
be constructed, allowing for sidewaysdynamic_cast
ing.During the construction of
A
then the dynamic type isA
regardless. This is because you would start calling member functions of derived classes and accessing derived member variables before it's been constructed, which would be UB and very bad.It doesn't work inside B, because B doesn't inherit from A