Ecco un riassunto su static_cast<>
e in dynamic_cast<>
particolare per quanto riguarda i puntatori. Questa è solo una carrellata di 101 livelli, non copre tutte le complessità.
static_cast <Tipo *> (ptr)
In questo modo il puntatore viene ptr
inserito e si tenta di eseguirne il cast sicuro in un puntatore di tipo Type*
. Questo cast viene eseguito in fase di compilazione. Eseguirà il cast solo se i tipi di tipo sono correlati. Se i tipi non sono correlati, verrà visualizzato un errore del compilatore. Per esempio:
class B {};
class D : public B {};
class X {};
int main()
{
D* d = new D;
B* b = static_cast<B*>(d); // this works
X* x = static_cast<X*>(d); // ERROR - Won't compile
return 0;
}
dynamic_cast <Tipo *> (ptr)
Questo tenta di nuovo di ptr
inserire il puntatore e di inserirlo in modo sicuro in un puntatore di tipo Type*
. Ma questo cast viene eseguito in fase di esecuzione, non in fase di compilazione. Poiché si tratta di un cast di runtime, è utile soprattutto se combinato con classi polimorfiche. In effetti, in casi certiani le classi devono essere polimorfiche affinché il cast sia legale.
I cast possono andare in una delle due direzioni: dalla base alla derivata (B2D) o dalla derivata alla base (D2B). È abbastanza semplice vedere come funzionano i cast di D2B in fase di esecuzione. O è ptr
stato derivato Type
o non lo era. Nel caso di D2B dynamic_cast <> s, le regole sono semplici. Puoi provare a trasmettere qualsiasi cosa a qualsiasi altra cosa, e se ptr
in realtà è derivato da Type
, otterrai un Type*
puntatore da dynamic_cast
. Altrimenti, otterrai un puntatore NULL.
Ma i lanci B2D sono un po 'più complicati. Considera il seguente codice:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void DoIt() = 0; // pure virtual
virtual ~Base() {};
};
class Foo : public Base
{
public:
virtual void DoIt() { cout << "Foo"; };
void FooIt() { cout << "Fooing It..."; }
};
class Bar : public Base
{
public :
virtual void DoIt() { cout << "Bar"; }
void BarIt() { cout << "baring It..."; }
};
Base* CreateRandom()
{
if( (rand()%2) == 0 )
return new Foo;
else
return new Bar;
}
int main()
{
for( int n = 0; n < 10; ++n )
{
Base* base = CreateRandom();
base->DoIt();
Bar* bar = (Bar*)base;
bar->BarIt();
}
return 0;
}
main()
non so quale tipo di oggetto CreateRandom()
restituirà, quindi il cast in stile C Bar* bar = (Bar*)base;
non è decisamente sicuro. Come hai potuto risolvere questo? Un modo sarebbe quello di aggiungere una funzione come bool AreYouABar() const = 0;
alla classe base e tornare true
da Bar
e false
versoFoo
. Ma c'è un altro modo: usare dynamic_cast<>
:
int main()
{
for( int n = 0; n < 10; ++n )
{
Base* base = CreateRandom();
base->DoIt();
Bar* bar = dynamic_cast<Bar*>(base);
Foo* foo = dynamic_cast<Foo*>(base);
if( bar )
bar->BarIt();
if( foo )
foo->FooIt();
}
return 0;
}
I cast vengono eseguiti in fase di esecuzione e funzionano eseguendo una query sull'oggetto (non è necessario preoccuparsi di come per ora), chiedendogli se è il tipo che stiamo cercando. In tal caso, dynamic_cast<Type*>
restituisce un puntatore; altrimenti restituisce NULL.
Affinché questa fusione base-derivata funzioni usando dynamic_cast<>
, Base, Foo e Bar devono essere quelli che lo Standard chiama tipi polimorfici . Per essere un tipo polimorfico, la tua classe deve avere almeno una virtual
funzione. Se le tue classi non sono tipi polimorfici, l'uso base-derivato dynamic_cast
non verrà compilato. Esempio:
class Base {};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile
return 0;
}
L'aggiunta di una funzione virtuale alla base, come un dtor virtuale, renderà i tipi polimorfici sia Base che Der:
class Base
{
public:
virtual ~Base(){};
};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // OK
return 0;
}
dynamic_cast<>
funziona dietro le quinte (o di quanto C ++ funziona), un buon libro (che è anche abbastanza facile da leggere per qualcosa di così tecnico) è "Inside the C ++ Object Model" di Lippman. Anche i libri di Stroustrup "Design and Evolution of C ++" e "The C ++ Programming Language" sono buone risorse, ma il libro di Lippman è dedicato al modo in cui il C ++ funziona "dietro le quinte".