Dynamic_cast
Dans le langage de programmation C++, l'opérateur dynamic_cast
est un membre du système de run-time type information (RTTI) qui effectue une conversion de type. Cependant, contrairement au cast hérité du langage C, une vérification du type est effectuée à l'exécution, et lèvera soit une exception dans le cas des références, soit un pointeur nul dans le cas des pointeurs, si les types ne sont pas compatibles. Donc, dynamic_cast
se comporte plus comme un convertisseur de type dans un langage tel que Java, plutôt qu'une conversion au sens du langage C, qui ne vérifie rien à l'exécution.
Exemple de code
Soit une classe A
, héritée par B
. Supposons qu'une méthode prenne un objet de type A
en argument, laquelle effectuerait quelques traitements supplémentaires si l'objet est en fait une instance de type B
. Ceci peut être réalisé à l'aide de dynamic_cast
comme suit :
#include <typeinfo> // Pour std::bad_cast
#include <iostream> // Pour std::cerr, etc.
class A
{
public:
// Puisque RTTI est incluse dans la table des méthodes virtuelles (la vtable),
// il devrait y avoir au moins une fonction virtuelle pure.
virtual void foo() = 0;
// autres membres...
};
class B : public A
{
public:
void foo() { /* ... */ }
void methodSpecificToB() { /* ... */ }
// autres membres...
};
void my_function(A& my_a)
{
try
{
B& my_b = dynamic_cast<B&>(my_a);
my_b.methodSpecificToB();
}
catch (const std::bad_cast& e)
{
std::cerr << e.what() << std::endl;
std::cerr << "Cet objet n'est pas de type B" << std::endl;
}
}
Une version équivalente de my_function
peut être écrite avec utilisation de pointeurs au lieu de références :
void my_function(A* my_a)
{
B* my_b = dynamic_cast<B*>(my_a);
if (my_b != nullptr)
my_b->methodSpecificToB();
else
std::cerr << "Cet objet n'est pas de type B" << std::endl;
}
Implémentation
Pour bien comprendre son fonctionnement, on peut regarder comment le dynamic_cast peut-être implémenté manuellement.
#include <iostream> // Pour std::cerr, etc.
class B;
class C;
class A
{
/// la méthode qui permettra de connaitre le type complet de notre objet
virtual const char get_type_for_dynamic_cast() const = 0;
public:
B* dynamic_cast_to_B()
{
if (get_type_for_dynamic_cast() != 'B')
return 0;
return static_cast<B*>(this);
}
C* dynamic_cast_to_C()
{
if (get_type_for_dynamic_cast() != 'C')
return 0;
return static_cast<C*>(this);
}
};
class B : public A
{
int data;
const char get_type_for_dynamic_cast() const { return 'B'; }
public:
B(int data) : data(data) { }
void methode() { std::cout << data << "\n"; }
};
class C : public A
{
char* data;
const char get_type_for_dynamic_cast() const { return 'C'; }
public:
C(char* data) : data(data) { }
void methode() { std::cout << data << "\n"; }
};
void ma_fonction(A* a)
{
if (B* b = a->dynamic_cast_to_B())
b->methode();
else if (C* c = a->dynamic_cast_to_C())
c->methode();
else {
// erreur de type
}
}
On voit qu'en mémoire, si nos classes possèdent déjà une VTable, le coût est à peu près nul puisque l'on a juste rajouté une méthode dans la VTable de chacune des classes. En termes de temps de calcul, le coût reste assez faible : un cast normal de pointeurs ne demande aucun calcul, alors qu'ici on doit appeler une méthode virtuelle pure (dont il faut aller lire l'adresse dans la VTable) et étudier sa valeur de retour.
Voir aussi
Articles connexes
- static cast (anglais)
- reinterpret cast (anglais)
- const cast (anglais)