dynamic_cast, opérateur

Convertit l’opérande expression en objet de type type-id.

Syntaxe

dynamic_cast < type-id > ( expression )

Notes

Il type-id doit s’agir d’un pointeur ou d’une référence à un type de classe précédemment défini ou à un « pointeur vers void ». Le type de expression doit être un pointeur si type-id est un pointeur ou une l-value si type-id est une référence.

Consultez static_cast pour obtenir une explication de la différence entre les conversions de cast statiques et dynamiques, et quand il est approprié d’utiliser chacun d’eux.

Il existe deux changements cassants dans le comportement du dynamic_cast code managé :

  • dynamic_cast vers un pointeur vers le type sous-jacent d’une énumération boxed échoue au moment de l’exécution, retournant 0 au lieu du pointeur converti.

  • dynamic_cast ne lève plus d’exception lorsqu’il type-id s’agit d’un pointeur intérieur vers un type valeur ; à la place, le cast échoue au moment de l’exécution. Le cast retourne la valeur de pointeur 0 au lieu de lever.

S’il type-id s’agit d’un pointeur vers une classe de base directe ou indirecte non ambiguë, expressionun pointeur vers le sous-objet unique de type type-id est le résultat. Par exemple :

// dynamic_cast_1.cpp
// compile with: /c
class B { };
class C : public B { };
class D : public C { };

void f(D* pd) {
   C* pc = dynamic_cast<C*>(pd);   // ok: C is a direct base class
                                   // pc points to C subobject of pd
   B* pb = dynamic_cast<B*>(pd);   // ok: B is an indirect base class
                                   // pb points to B subobject of pd
}

Ce type de conversion est appelé « upcast », car il déplace un pointeur vers le haut d’une hiérarchie de classes, d’une classe dérivée vers une classe dont elle est dérivée. Une mise en service est une conversion implicite.

Si type-id est void*, une case activée d’exécution est effectuée pour déterminer le type réel de expression. Le résultat est un pointeur vers l’objet complet pointé par expression. Par exemple :

// dynamic_cast_2.cpp
// compile with: /c /GR
class A {virtual void f();};
class B {virtual void f();};

void f() {
   A* pa = new A;
   B* pb = new B;
   void* pv = dynamic_cast<void*>(pa);
   // pv now points to an object of type A

   pv = dynamic_cast<void*>(pb);
   // pv now points to an object of type B
}

Si type-id ce n’est pas void*le cas, une case activée d’exécution est effectuée pour voir si l’objet pointé par expression peut être converti en type pointé par type-id.

Si le type d’objet expression est une classe de base du type , type-idun case activée d’exécution est fait pour voir si expression en fait pointe vers un objet complet du type de type-id. Si cela est vrai, le résultat est un pointeur vers un objet complet du type de type-id. Par exemple :

// dynamic_cast_3.cpp
// compile with: /c /GR
class B {virtual void f();};
class D : public B {virtual void f();};

void f() {
   B* pb = new D;   // unclear but ok
   B* pb2 = new B;

   D* pd = dynamic_cast<D*>(pb);   // ok: pb actually points to a D
   D* pd2 = dynamic_cast<D*>(pb2);   // pb2 points to a B not a D
}

Ce type de conversion est appelé « downcast », car il déplace un pointeur vers le bas d’une hiérarchie de classes, d’une classe donnée à une classe dérivée de celle-ci.

Dans les cas d’héritage multiple, des possibilités d’ambiguïté sont introduites. Considérez la hiérarchie de classes indiquée dans la figure suivante.

Pour les types CLR, dynamic_cast entraîne une opération sans opération si la conversion peut être effectuée implicitement ou une instruction MSILisinst, qui effectue une case activée dynamique et retourne nullptr si la conversion échoue.

L’exemple suivant utilise dynamic_cast pour déterminer si une classe est une instance de type particulier :

// dynamic_cast_clr.cpp
// compile with: /clr
using namespace System;

void PrintObjectType( Object^o ) {
   if( dynamic_cast<String^>(o) )
      Console::WriteLine("Object is a String");
   else if( dynamic_cast<int^>(o) )
      Console::WriteLine("Object is an int");
}

int main() {
   Object^o1 = "hello";
   Object^o2 = 10;

   PrintObjectType(o1);
   PrintObjectType(o2);
}

Diagram that shows multiple inheritance.

Le diagramme montre une hiérarchie de classes avec A comme classe de base de B, qui est une classe de base de D. A est également une classe de base pour C, qui est une classe de base pour D. La classe D hérite à la fois de B et C.

Un pointeur vers un objet de type D peut être converti en toute sécurité vers B ou C. Toutefois, s’il D est casté pour pointer vers un A objet, quelle instance A entraînerait-elle ? Cela entraînerait une erreur de cast ambiguë. Pour contourner ce problème, vous pouvez effectuer deux casts non ambigus. Par exemple :

// dynamic_cast_4.cpp
// compile with: /c /GR
class A {virtual void f();};
class B : public A {virtual void f();};
class C : public A {virtual void f();};
class D : public B, public C {virtual void f();};

void f() {
   D* pd = new D;
   A* pa = dynamic_cast<A*>(pd);   // C4540, ambiguous cast fails at runtime
   B* pb = dynamic_cast<B*>(pd);   // first cast to B
   A* pa2 = dynamic_cast<A*>(pb);   // ok: unambiguous
}

D’autres ambiguïtés peuvent être introduites lorsque vous utilisez des classes de base virtuelles. Considérez la hiérarchie de classes indiquée dans la figure suivante.

Diagram of a class hierarchy that shows virtual base classes.

Le diagramme montre les classes A, B, C, D et E organisées comme suit : La classe A est une classe de base de B. Classes C et E dérivent chacune de B. Classe E hérite également de D, qui hérite de la classe B, qui hérite de la classe A.

Hiérarchie de classes montrant des classes de base virtuelles

Dans cette hiérarchie, A est une classe de base virtuelle. Étant donné une instance de classe E et un pointeur vers le A sous-objet, un dynamic_cast pointeur B échoue en raison d’ambiguïté. Vous devez d’abord revenir à l’objet complet E , puis sauvegarder la hiérarchie de manière non ambiguë pour atteindre l’objet correct B .

Considérez la hiérarchie de classes indiquée dans la figure suivante.

Diagram of a class hierarchy that shows duplicate base classes.

Le diagramme montre les classes A, B, C, D et E organisées comme suit : La classe B dérive de la classe A. La classe C dérive de la classe A. la classe D dérive de la classe B. La classe E dérive de la classe C, qui dérive de la classe A. Dans ce cas, la classe de base dupliquée est la classe A, qui est directement ou indirectement héritée par toutes les autres classes. La classe A est héritée directement par les classes B et C, et indirectement par classe D via la classe B, et indirectement par la classe E via la classe C, et indirectement dans la classe D via la classe B.

Hiérarchie de classes montrant des classes de base en double

Étant donné un objet de type E et un pointeur vers le D sous-objet, pour naviguer du D sous-objet vers le sous-objet le plus A à gauche, trois conversions peuvent être effectuées. Vous pouvez effectuer une dynamic_cast conversion du D pointeur vers un E pointeur, puis une conversion (soit dynamic_cast une conversion implicite) vers EB, puis une conversion implicite en BA. Par exemple :

// dynamic_cast_5.cpp
// compile with: /c /GR
class A {virtual void f();};
class B : public A {virtual void f();};
class C : public A { };
class D {virtual void f();};
class E : public B, public C, public D {virtual void f();};

void f(D* pd) {
   E* pe = dynamic_cast<E*>(pd);
   B* pb = pe;   // upcast, implicit conversion
   A* pa = pb;   // upcast, implicit conversion
}

L’opérateur dynamic_cast peut également être utilisé pour effectuer un « cast croisé ». À l’aide de la même hiérarchie de classes, il est possible de convertir un pointeur, par exemple, du B sous-objet au D sous-objet, tant que l’objet complet est de type E.

Compte tenu des casts croisés, il est possible d’effectuer la conversion d’un pointeur vers D un pointeur vers le sous-objet le plus A à gauche en deux étapes. Vous pouvez effectuer un cast croisé de D vers B, puis une conversion implicite de B vers A. Par exemple :

// dynamic_cast_6.cpp
// compile with: /c /GR
class A {virtual void f();};
class B : public A {virtual void f();};
class C : public A { };
class D {virtual void f();};
class E : public B, public C, public D {virtual void f();};

void f(D* pd) {
   B* pb = dynamic_cast<B*>(pd);   // cross cast
   A* pa = pb;   // upcast, implicit conversion
}

Une valeur de pointeur Null est convertie en valeur de pointeur Null du type de destination par dynamic_cast.

Lorsque vous utilisez dynamic_cast < type-id > ( expression ), si expression vous ne pouvez pas être converti en toute sécurité en typetype-id, l’exécution case activée provoque l’échec du cast. Par exemple :

// dynamic_cast_7.cpp
// compile with: /c /GR
class A {virtual void f();};
class B {virtual void f();};

void f() {
   A* pa = new A;
   B* pb = dynamic_cast<B*>(pa);   // fails at runtime, not safe;
   // B not derived from A
}

La valeur d’un type de pointeur ayant échoué est le pointeur Null. Un cast ayant échoué pour référencer le type lève une exception bad_cast. Si expression elle ne pointe pas vers ou référence un objet valide, une __non_rtti_object exception est levée.

Consultez typeid pour obtenir une explication de l’exception __non_rtti_object .

Exemple

L’exemple suivant crée le pointeur de classe de base (struct A) vers un objet (struct C). Cela, ainsi que le fait qu’il existe des fonctions virtuelles, active le polymorphisme runtime.

L’exemple appelle également une fonction non virtuelle dans la hiérarchie.

// dynamic_cast_8.cpp
// compile with: /GR /EHsc
#include <stdio.h>
#include <iostream>

struct A {
    virtual void test() {
        printf_s("in A\n");
   }
};

struct B : A {
    virtual void test() {
        printf_s("in B\n");
    }

    void test2() {
        printf_s("test2 in B\n");
    }
};

struct C : B {
    virtual void test() {
        printf_s("in C\n");
    }

    void test2() {
        printf_s("test2 in C\n");
    }
};

void Globaltest(A& a) {
    try {
        C &c = dynamic_cast<C&>(a);
        printf_s("in GlobalTest\n");
    }
    catch(std::bad_cast) {
        printf_s("Can't cast to C\n");
    }
}

int main() {
    A *pa = new C;
    A *pa2 = new B;

    pa->test();

    B * pb = dynamic_cast<B *>(pa);
    if (pb)
        pb->test2();

    C * pc = dynamic_cast<C *>(pa2);
    if (pc)
        pc->test2();

    C ConStack;
    Globaltest(ConStack);

   // fails because B knows nothing about C
    B BonStack;
    Globaltest(BonStack);
}
in C
test2 in B
in GlobalTest
Can't cast to C

Voir aussi

Opérateurs de casting
Mots clés