views:

88

answers:

7
#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?

+1  A: 

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

Andrey
+6  A: 

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.

mkaes
+1  A: 

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.

Pedro d'Aquino
+1  A: 

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

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

and search for dynamic_cast

Patrick
+4  A: 

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`
Prasoon Saurav
+11  A: 

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
} 
Ben Voigt
@Ben: Excellent!
Johann Gerell
A: 

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).

Matthieu M.