Voici un aperçu static_cast<>
et en dynamic_cast<>
particulier en ce qui concerne les pointeurs. Ceci est juste un aperçu de 101 niveaux, il ne couvre pas toutes les subtilités.
static_cast <Type *> (ptr)
Cela prend le pointeur ptr
et tente de le convertir en toute sécurité en un pointeur de type Type*
. Cette distribution est effectuée au moment de la compilation. Il n'effectuera le cast que si les types de type sont liés. Si les types ne sont pas liés, vous obtiendrez une erreur du compilateur. Par exemple:
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 <Type *> (ptr)
Cela tente à nouveau de prendre le pointeur ptr
et de le convertir en toute sécurité en un pointeur de type Type*
. Mais ce cast est exécuté au moment de l'exécution, pas au moment de la compilation. Comme il s'agit d'un cast au moment de l'exécution, il est particulièrement utile lorsqu'il est combiné avec des classes polymorphes. En fait, dans les cas certiens, les classes doivent être polymorphes pour que la distribution soit légale.
Les moulages peuvent aller dans l'une des deux directions suivantes: de la base au dérivé (B2D) ou du dérivé à la base (D2B). C'est assez simple pour voir comment les casts D2B fonctionneraient au moment de l'exécution. Soit ptr
était dérivé de, Type
soit il ne l'était pas. Dans le cas des D2B dynamic_cast <>, les règles sont simples. Vous pouvez essayer de convertir n'importe quoi en autre chose, et si elle ptr
est en fait dérivée de Type
, vous récupérerez un Type*
pointeur dynamic_cast
. Sinon, vous obtiendrez un pointeur NULL.
Mais les lancers B2D sont un peu plus compliqués. Considérez le code suivant:
#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()
ne peut pas dire quel type d'objet CreateRandom()
retournera, donc le cast de style C Bar* bar = (Bar*)base;
n'est décidément pas de type sécurisé. Comment pouvez-vous résoudre ce problème? Une façon serait d'ajouter une fonction comme bool AreYouABar() const = 0;
à la classe de base et de retourner true
de Bar
et false
de Foo
. Mais il existe un autre moyen: utilisez 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;
}
Les casts s'exécutent au moment de l'exécution et fonctionnent en interrogeant l'objet (pas besoin de s'inquiéter de savoir comment pour le moment), en lui demandant s'il est le type que nous recherchons. Si c'est le cas, dynamic_cast<Type*>
renvoie un pointeur; sinon, il renvoie NULL.
Pour que cette conversion de base en dérivé fonctionne avec dynamic_cast<>
, Base, Foo et Bar doivent être ce que le Standard appelle des types polymorphes . Pour être de type polymorphe, votre classe doit avoir au moins une virtual
fonction. Si vos classes ne sont pas des types polymorphes, l'utilisation de base à dérivée de dynamic_cast
ne sera pas compilée. Exemple:
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'ajout d'une fonction virtuelle à la base, comme un dtor virtuel, rendra les types polymorphes Base et 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<>
fonctionne dans les coulisses (ou combien de C ++ fonctionne), un bon livre (qui est aussi assez facile à lire pour quelque chose d'aussi technique) est "Inside the C ++ Object Model" de Lippman. Les livres "Design and Evolution of C ++" et "The C ++ Programming Language" de Stroustrup sont également de bonnes ressources, mais le livre de Lippman est dédié à la manière dont C ++ fonctionne "en coulisses".