12
#include <iostream> using namespace std; class X{ public: virtual void f(){} }; class Y { public: virtual void g() {} }; int main() { X * x = new X(); Y* y = dynamic_cast<Y*>(x); //A // Y* y = static_cast<Y*>(x); //B cout << y << endl; } 

A compiles whereas B doesn't. I understand why B doesn't get compiled but why does A get compiled although X and Y are completely unrelated types?

7 Answers 7

25

This is why dynamic_cast is allowed between unrelated types:

class X{ public: virtual void f(){} }; class Y { public: virtual void g() {} }; class Z : public X, public Y {}; int main() { X* x = new Z(); Y* y = dynamic_cast<Y*>(x); // compiles and yields non-null pointer } 
Sign up to request clarification or add additional context in comments.

Comments

5

The dynamic cast uses the runtime type information. So this is legal do this case but it will return a null pointer. The static cast is evaluated by the compiler.

Comments

1

Compiler doesn't care, because it is dynamic_cast. y will be NULL after the cast.

Comments

1

dynamic_cast performs the type checking at runtime, using RTTI, while static_cast performs it at compile time. So what you'd get if you ran A would be y == NULL.

I suggest you read up on C++ type casts.

Comments

1

dynamic cast returns NULL if it can't complete the cast:

http://www.cplusplus.com/doc/tutorial/typecasting/

and search for dynamic_cast

Comments

1

If [by using dynamic cast] you attempt to cast to a pointer type, and that type is not an actual type of the argument object, then the result of the cast will be NULL.

Y* y = dynamic_cast<Y*>(x); // NULL because `X` is not a `Y` 

Comments

1

There is a huge difference between static_cast and dynamic_cast, I'll just reduce the discussion to the objects world.

A static_cast may be used for the (infamous) up-cast. That is:

void foo(Base& b) { Derived& d = static_cast<Derived&>(b); } 

The compiler can assess whether this is legal or not, because having the definition of Derived it knows whether or not Derived is actually a descendent of Base.

For non-trivial hierarchies:

struct Base {}; struct D1: Base {}; struct D2: Base {}; struct Derived: D1, D2 {}; 

This would yield an error: the compiler would not know from which of the bases (the one from D1 or the one from D2 you came from).

Or if you used virtual inheritance:

struct VDerived: virtual VBase {}; 

the compiler would need run-time information, and thus the compilation would fail too.

A dynamic_cast however is much more clever, though this comes at the cost of runtime-overhead. The compiler generates information about the objects generally know as RTTI (RunTime Type Information), that the dynamic_cast will explore to allow:

Cast depending on the true runtime type:

// will throw `bad_cast` if b is not a `Derived` void foo(Base& b) { Derived& d = dynamic_cast<Derived&>(b); } 

Cast accross branches:

struct X {}; struct Y {}; void foo(X* x) { Y* y = dynamic_cast<Y*>(x); } // If I add: struct Z: X, Y {}; // then if x is in fact a Z, y will be non-null 

Cast when there is virtual inheritance involved.

void bar(VBase* vb) { VDerived* vd = dynamic_cast<VDerived*>(vb); } 

Cast to void* to get the true address of the object (this is somewhat special).

void foobar(X* x) { void* xAddr = dynamic_cast<void*>(x); Y* y = dynamic_cast<Y*>(y); void* yAddr = dynamic_cast<void*>(y); Z* z = dynamic_cast<Z*>(x); void* zAddr = dynamic_cast<void*>(z); if (z) { assert(xAddr == yAddr && xAddr == zAddr); } } 

Note that this still doesn't work if there is an ambiguity because of the hierarchy (as with the D1/D2 example above).

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.