Accueil🇫🇷Chercher

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

Liens externes

Cet article est issu de wikipedia. Text licence: CC BY-SA 4.0, Des conditions supplémentaires peuvent s’appliquer aux fichiers multimédias.