Aquí hay un resumen static_cast<>
y dynamic_cast<>
específicamente en lo que respecta a los punteros. Este es solo un resumen de 101 niveles, no cubre todas las complejidades.
static_cast <Tipo *> (ptr)
Esto toma el puntero ptr
e intenta convertirlo de manera segura en un puntero de tipo Type*
. Este reparto se realiza en tiempo de compilación. Solo realizará la conversión si los tipos de tipos están relacionados. Si los tipos no están relacionados, obtendrá un error de compilación. Por ejemplo:
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)
Esto intenta nuevamente tomar el puntero ptr
y convertirlo de manera segura en un puntero de tipo Type*
. Pero este reparto se ejecuta en tiempo de ejecución, no en tiempo de compilación. Debido a que este es un lanzamiento en tiempo de ejecución, es útil especialmente cuando se combina con clases polimórficas. De hecho, en casos certian las clases deben ser polimórficas para que el reparto sea legal.
Los moldes pueden ir en una de dos direcciones: de base a derivada (B2D) o de derivada a base (D2B). Es lo suficientemente simple como para ver cómo los moldes D2B funcionarían en tiempo de ejecución. O ptr
se derivaba de Type
o no. En el caso de D2B dynamic_cast <> s, las reglas son simples. Puede intentar lanzar cualquier cosa a otra cosa, y si ptr
de hecho se derivó Type
, obtendrá un Type*
puntero de regreso dynamic_cast
. De lo contrario, obtendrá un puntero NULL.
Pero los modelos B2D son un poco más complicados. Considere el siguiente código:
#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()
no puedo decir qué tipo de objeto CreateRandom()
volverá, por lo que el reparto de estilo C Bar* bar = (Bar*)base;
definitivamente no es seguro para escribir. ¿Cómo puedes arreglar esto? Una forma sería agregar una función como bool AreYouABar() const = 0;
a la clase base y regresar true
desde Bar
y false
desde Foo
. Pero hay otra forma: use 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;
}
Los lanzamientos se ejecutan en tiempo de ejecución y funcionan consultando el objeto (no hay que preocuparse por cómo hacerlo por ahora), preguntándole si es del tipo que estamos buscando. Si es así, dynamic_cast<Type*>
devuelve un puntero; de lo contrario, devuelve NULL.
Para que esta conversión de base a derivada funcione dynamic_cast<>
, Base, Foo y Bar deben ser lo que el Estándar llama tipos polimórficos . Para ser un tipo polimórfico, su clase debe tener al menos una virtual
función. Si sus clases no son tipos polimórficos, el uso de base a derivada de dynamic_cast
no se compilará. Ejemplo:
class Base {};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile
return 0;
}
Agregar una función virtual a la base, como un dtor virtual, hará que los tipos polimórficos Base y 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<>
funciona detrás de escena (o cuánto funciona C ++), un buen libro (que también es bastante fácil de leer para algo tan técnico) es "Inside the C ++ Object Model" de Lippman. También los libros "Diseño y evolución de C ++" y "El lenguaje de programación C ++" de Stroustrup son buenos recursos, pero el libro de Lippman está dedicado a cómo funciona C ++ "detrás de escena".