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’iltype-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ë, expression
un 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 vérification au moment de l’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 vérification au moment de l’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 de , une vérification au moment de type-id
l’exécution est effectuée pour voir si expression
elle pointe réellement 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 MSIL isinst
, qui effectue une vérification 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);
}
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.
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.
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 E
B
, puis une conversion implicite en B
A
. 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 type type-id
, la vérification au moment de l’exécution entraîne 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