Operatore dynamic_cast

Converte l'operando expression in un oggetto di tipo type-id.

Sintassi

dynamic_cast < type-id > ( expression )

Osservazioni:

type-id Deve essere un puntatore o un riferimento a un tipo di classe definito in precedenza o un "puntatore a void". Il tipo di expression deve essere un puntatore se type-id è un puntatore o un l-value se type-id è un riferimento.

Vedere static_cast per una spiegazione della differenza tra conversioni di cast statico e dinamico e quando è opportuno usarle.

Esistono due modifiche di rilievo nel comportamento di dynamic_cast nel codice gestito:

  • dynamic_cast a un puntatore al tipo sottostante di un'enumerazione boxed avrà esito negativo in fase di esecuzione, restituendo 0 anziché il puntatore convertito.

  • dynamic_cast non genererà più un'eccezione quando type-id è un puntatore interno a un tipo di valore, ma il cast non riesce in fase di esecuzione. Il cast restituisce il valore del puntatore 0 anziché generare.

Se type-id è un puntatore a una classe base diretta o indiretta accessibile senza ambiguità di expression, un puntatore al sottooggetto univoco di tipo type-id è il risultato. Ad esempio:

// 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
}

Questo tipo di conversione è denominato "upcast" perché sposta un puntatore su una gerarchia di classi, da una classe derivata a una classe derivata da cui è derivato. Un upcast è una conversione implicita.

Se type-id è void*, viene eseguito un controllo di runtime per determinare il tipo effettivo di expression. Il risultato è un puntatore all'oggetto completo a expressioncui punta . Ad esempio:

// 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
}

Se type-id non void*è , viene eseguito un controllo di runtime per verificare se l'oggetto a expression cui punta può essere convertito nel tipo a type-idcui punta .

Se il tipo di expression è una classe base del tipo di type-id, viene eseguito un controllo di runtime per verificare se expression effettivamente punta a un oggetto completo del tipo di type-id. Se è true, il risultato è un puntatore a un oggetto completo del tipo di type-id. Ad esempio:

// 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
}

Questo tipo di conversione è denominato "downcast" perché sposta un puntatore verso il basso in una gerarchia di classi, da una determinata classe a una classe derivata da essa.

In caso di ereditarietà multipla, vengono introdotte possibilità di ambiguità. Si consideri la gerarchia di classi illustrata nella figura seguente.

Per i tipi CLR, dynamic_cast restituisce un valore no-op se la conversione può essere eseguita in modo implicito o un'istruzione MSIL isinst , che esegue un controllo dinamico e restituisce nullptr se la conversione non riesce.

Nell'esempio seguente viene dynamic_cast usato per determinare se una classe è un'istanza di un particolare tipo:

// 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.

Il diagramma mostra una gerarchia di classi con A come classe base di B, che è una classe base di D. Un è anche una classe base per C, che è una classe base per la classe D. La classe D eredita sia da B che da C.

Un puntatore a un oggetto di tipo D può essere eseguito in modo sicuro su B o C. Tuttavia, se viene eseguito il D cast per puntare a un A oggetto, quale istanza di A verrebbe restituita? In questo modo si verifica un errore di cast ambiguo. Per risolvere questo problema, è possibile eseguire due cast non ambigui. Ad esempio:

// 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
}

È possibile introdurre ulteriori ambiguità quando si usano classi di base virtuali. Si consideri la gerarchia di classi illustrata nella figura seguente.

Diagram of a class hierarchy that shows virtual base classes.

Il diagramma mostra le classi A, B, C, D ed E disposte nel modo seguente: la classe A è una classe base di B. Classi C ed E derivano da B. La classe E eredita anche da D, che eredita dalla classe B, che eredita dalla classe A.

Gerarchia delle classi con classi di base virtuali

In questa gerarchia è A una classe di base virtuale. Data un'istanza della classe E e un puntatore al A sottooggetto, un dynamic_cast oggetto a un puntatore a non riesce a B causa di ambiguità. È innanzitutto necessario eseguire il cast all'oggetto completo E , quindi eseguire il backup della gerarchia, in modo non ambiguo, per raggiungere l'oggetto corretto B .

Si consideri la gerarchia di classi illustrata nella figura seguente.

Diagram of a class hierarchy that shows duplicate base classes.

Il diagramma mostra le classi A, B, C, D ed E disposte nel modo seguente: la classe B deriva dalla classe A. La classe C deriva dalla classe A. la classe D deriva dalla classe B. La classe E deriva dalla classe C, che deriva dalla classe A. In questo caso, la classe base duplicata è la classe A, che è ereditata direttamente o indirettamente da tutte le altre classi. La classe A viene ereditata direttamente dalle classi B e C e indirettamente dalla classe D tramite la classe B e indirettamente dalla classe E tramite la classe C e indirettamente nella classe D tramite la classe B.

Gerarchia delle classi con classi di base duplicate

Dato un oggetto di tipo E e un puntatore al D sottooggetto, per spostarsi dall'oggetto secondario all'oggetto D secondario più A a sinistra, è possibile eseguire tre conversioni. È possibile eseguire una dynamic_cast conversione dal D puntatore a un E puntatore, quindi una conversione (dynamic_casto una conversione implicita) da a Be infine una conversione implicita da EB a A. Ad esempio:

// 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'operatore dynamic_cast può essere usato anche per eseguire un "cross cast". Usando la stessa gerarchia di classi, è possibile eseguire il cast di un puntatore, ad esempio, dal B sottooggetto al D sottooggetto, purché l'oggetto completo sia di tipo E.

Considerando i cast incrociati, è possibile eseguire la conversione da un puntatore a D un puntatore al sottooggetto più A a sinistra in soli due passaggi. È possibile eseguire un cast incrociato da D a B, quindi una conversione implicita da B a A. Ad esempio:

// 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
}

Un valore del puntatore Null viene convertito nel valore del puntatore Null del tipo di destinazione da dynamic_cast.

Quando si usa dynamic_cast < type-id > ( expression ), se expression non è possibile convertire in modo sicuro in tipo type-id, il controllo di runtime causa l'esito negativo del cast. Ad esempio:

// 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
}

Il valore di un cast non riuscito al tipo di puntatore è il puntatore Null. Un cast non riuscito al tipo riferimento genera un'eccezione bad_cast. Se expression non punta o fa riferimento a un oggetto valido, viene generata un'eccezione __non_rtti_object .

Per una spiegazione dell'eccezione__non_rtti_object, vedere typeid.

Esempio

L'esempio seguente crea il puntatore alla classe di base (struct A) a un oggetto (struct C). Questo, oltre al fatto che ci sono funzioni virtuali, abilita il polimorfismo di runtime.

L'esempio chiama anche una funzione non virtuale nella gerarchia.

// 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

Vedi anche

Operatori di cast
Parole chiave