C'è una differenza enorme tra static_cast
e dynamic_cast
, mi limiterò a riduco la discussione al mondo degli oggetti.
A static_cast
può essere utilizzato per il (infame) up-cast. Cioè:
void foo(Base& b) { Derived& d = static_cast<Derived&>(b); }
Il compilatore può valutare se questo è legale o no, perché avere la definizione di Derived
si sa o meno Derived
è in realtà un discendente di Base
.
per le gerarchie non banali:
struct Base {};
struct D1: Base {};
struct D2: Base {};
struct Derived: D1, D2 {};
Ciò produrrebbe un errore: il compilatore non saprebbe da quale delle basi (quella da D1
o quello da D2
siete venuti).
O se si è utilizzato virtual
eredità:
struct VDerived: virtual VBase {};
il compilatore avrebbe bisogno di informazioni di runtime, e quindi la compilazione fallirebbe anche.
A dynamic_cast
tuttavia è molto più intelligente, anche se ciò comporta un sovraccarico di runtime. Il compilatore genera informazioni sugli oggetti generalmente conosciuto come RTTI (Runtime Type informazioni), che il dynamic_cast
esplorerà per consentire:
Fusioni seconda del vero tipo runtime:
// will throw `bad_cast` if b is not a `Derived`
void foo(Base& b) { Derived& d = dynamic_cast<Derived&>(b); }
Fusioni attraversato rami:
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
Trasmetti quando è coinvolta l'ereditarietà virtuale.
void bar(VBase* vb) { VDerived* vd = dynamic_cast<VDerived*>(vb); }
Fusioni per void*
per ottenere il vero indirizzo dell'oggetto (questo è un po 'speciale).
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); }
}
noti che questa non funziona se c'è un'ambiguità causa della gerarchia (come nell'esempio D1
/D2
sopra).