Reputation: 1262
I recently had a problem with casts and multiple inheritance: I needed to cast a Base*
to Unrelated*
, because a specific Derived
class derives the Unrelated
class.
This is a short example:
#include <iostream>
struct Base{
virtual ~Base() = default;
};
struct Unrelated{
float test = 111;
};
struct Derived : Base,Unrelated{};
int main(){
Base* b = new Derived;
Unrelated* u1 = (Unrelated*)b;
std::cout << u1->test << std::endl; //outputs garbage
Unrelated* y = dynamic_cast<Unrelated*>(b);
std::cout << y->test << std::endl; //outputs 111
}
The first cast clearly doesnt work, but the second one did work.
My question is: Why did the second cast work? Shouldnt dynamic_cast
only work for cast to a related class type? I thought there wasnt any information about Unrelated
at runtime because it is not polymorphic.
Edit: I used colirus gcc for the example.
Upvotes: 2
Views: 1267
Reputation: 145204
The first cast (Unrelated*)b
doesn't work because you're treating the Base
class sub-object, containing probably just a vtable pointer, as an Unrelated
, containing a float
.
Instead you can cast down and up, static_cast<Unrelated*>( static_cast<Derived*>( b ) )
.
And this is what dynamic_cast
does for you, since Base
is a polymorphic type (at least one virtual method) which allows dynamic_cast
to inspect the type of the most derived object.
In passing, dynamic_cast<void*>( b )
would give you a pointer to the most derived object.
However, since you know the types there's no need to invoke the slight overhead of a dynamic_cast
: just do the down- and up-casts.
Instead of the C style cast (Unrelated*)b
you should use the corresponding C++ named cast or casts, because C style casts of pointers can do things you'd not expect, and because the effect can change completely when types are changed during maintenance.
The C style cast will maximum do 2 C++ named casts. In this case the C style cast corresponds to a reinterpret_cast
. The compiler will not allow any other named cast here.
Which is a warning sign. ;-)
In contrast, the down- and up-casts are static_cast
s, which usually are benign casts.
All that said, the best is to almost completely avoid casts by using the top secret technique:
Don't throw away type information in the first place.
I.e., in the example code, just use Derived*
as the type of the pointer.
Upvotes: 5
Reputation: 308101
With multiple inheritance, the Derived
object consists of two sub-objects, one Base
and one Unrelated
. The compiler knows how to access whichever part of the object it needs, typically by adding an offset to the pointer (but that's an implementation detail). It can only do that when it knows the actual type of a pointer. By using a C-style cast, you've told the compiler to ignore the actual type and treat that pointer value as a pointer to something else. It no longer has the information necessary to properly access the sub-object you desire, and it fails.
dynamic_cast
allows the compiler to use run-time information about the object to locate the proper sub-object contained within it. If you were to output or examine the pointer values themselves, you'd see that they are different.
Upvotes: 2
Reputation: 136208
dynamic_cast
works because the dynamic type of object pointed to by a pointer to its base class is related to Unrelated
.
Keep in mind that dynamic_cast
requires a virtual table to inspect the inheritance tree of the object at run-time.
The C-style cast (Unrelated*)b
does not work because the C-style cast does const_cast
, static_cast
, reinterpret_cast
and more, but it does not do dynamic_cast
.
I would suggest avoiding C-style casts in C++ code because they do so many things as opposed to precise C++ casts. My colleagues who insist on using C-style cast still occasionally get them wrong.
Upvotes: 8