#include <i开发者_如何学JAVAostream>
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?
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
}
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.
Compiler doesn't care, because it is dynamic_cast
. y
will be NULL
after the cast.
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.
dynamic cast returns NULL if it can't complete the cast:
http://www.cplusplus.com/doc/tutorial/typecasting/
and search for dynamic_cast
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`
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).
精彩评论