Condividi tramite


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);
}

Diagramma che mostra l'ereditarietà multipla.

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.

Diagramma di una gerarchia di classi che mostra le classi di base virtuali.

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.

Diagramma di una gerarchia di classi che mostra classi di base duplicate.

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 E B 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