Operador dynamic_cast

Converte o operando expression em um objeto do tipo type-id.

Sintaxe

dynamic_cast < type-id > ( expression )

Comentários

type-id deve ser um ponteiro ou uma referência a um tipo previamente definido da classe ou a um "ponteiro para nulo". O tipo de expression deve ser um ponteiro se type-id for um ponteiro, ou um l-value se type-id for uma referência.

Consulte static_cast para obter uma explicação da diferença entre conversões de conversão estática e dinâmica e quando é apropriado usar cada uma delas.

Há duas importantes alterações no comportamento de dynamic_cast no código gerenciado:

  • dynamic_cast para um ponteiro para o tipo subjacente de um enum encaixotado falhará no runtime, retornando 0 ao invés do ponteiro convertido.

  • dynamic_cast não lançará mais uma exceção quando type-id for um ponteiro interno para um tipo de valor; em vez disso, a conversão falhará em tempo de execução. O cast retorna o valor de ponteiro 0 em vez de arremessar.

Se type-id for um ponteiro para uma classe base direta ou indireta acessível e inequívoca de expression, então um ponteiro para o subobjeto exclusivo do tipo type-id será o resultado. Por exemplo:

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

Esse tipo de conversão é chamado de "upcast" porque move um ponteiro para cima de uma hierarquia de classe, de uma classe derivada para uma classe da qual é derivada. Um upcast é uma conversão implícita.

Se type-id é nulo*, uma verificação de tempo de execução será feita para determinar o tipo real de expression. O resultado é um ponteiro para o objeto completo apontado por expression. Por exemplo:

// 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 não estiver, uma verificação em tempo de execução será feita para ver se o objeto apontado por pode ser convertido para o tipo apontado por expressiontype-id.void*

Se o tipo de expression é uma classe base do tipo type-id, uma verificação de tempo de execução será feita para verificar se expression realmente aponta para um objeto completo do tipo type-id. Se isso ocorrer, o resultado é um ponteiro para um objeto completo do tipo type-id. Por exemplo:

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

Esse tipo de conversão é chamada de "downcast", pois move um ponteiro para uma hierarquia da classe abaixo, de uma classe determinada para uma classe derivada dela.

Em casos de herança múltipla, as possibilidades de ambiguidade são introduzidas. Considere a hierarquia da classe mostrada na figura a seguir.

Para tipos de CLR, dynamic_cast resulta em uma instrução no-op se a conversão puder ser executada implicitamente ou uma instrução isinst MSIL, que executa uma verificação dinâmica e retorna nullptr se a conversão falhar.

O seguinte exemplo usa dynamic_cast para determinar se uma classe é uma instância do tipo específico:

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

O diagrama mostra uma hierarquia de classes com A como uma classe base de B, que é uma classe base de D. A também é uma classe base para C, que é uma classe base para D. A classe D herda de B e C.

Um ponteiro para um objeto do tipo D pode seguramente ser gerado em B ou C. No entanto, se D for gerado para apontar para um objeto A, qual instância de A resultaria? Isso resultará em um erro ambíguo de geração. Para contornar esse problema, você pode executar duas conversões inequívocas. Por exemplo:

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

As ambiguidades adicionais podem ser introduzidas quando você usar classes base virtuais. Considere a hierarquia da classe mostrada na figura a seguir.

Diagram of a class hierarchy that shows virtual base classes.

O diagrama mostra as classes A, B, C, D e E organizadas da seguinte forma: A classe A é uma classe base de B. As classes C e E derivam de B. A classe E também herda de D, que herda da classe B, que herda da classe A.

Hierarquia de classes que mostra classes base virtuais

Nesta hierarquia, A é uma classe base virtual. Dada uma instância de classe E e um ponteiro para o A subobjeto, um dynamic_cast ponteiro para B um falha devido à ambiguidade. Primeiro você deve converter de volta ao objeto completo E, então trabalhar até a hierarquia, de maneira não ambígua, para alcançar o objeto correto B.

Considere a hierarquia da classe mostrada na figura a seguir.

Diagram of a class hierarchy that shows duplicate base classes.

O diagrama mostra as classes A, B, C, D e E organizadas da seguinte forma: A classe B deriva da classe A. A classe C deriva da classe A.class D deriva da classe B. A classe E deriva da classe C, que deriva da classe A. Nesse caso, a classe base duplicada é a classe A, que é direta ou indiretamente herdada por todas as outras classes. A classe A é herdada diretamente pelas classes B e C, e indiretamente pela classe D via classe B, e indiretamente pela classe E via classe C, e indiretamente na classe D via classe B.

Hierarquia de classes que mostra classes base duplicadas

Dado um objeto de tipo E e um ponteiro para o subobjeto D, para navegar do subobjeto D ao subobjeto mais à esquerda A, três conversões podem ser feitas. Você pode executar uma conversão dynamic_cast do ponteiro D para um ponteiro E e uma conversão (dynamic_cast ou uma conversão implícita) de E para B e, por fim, uma conversão implícita de B para A. Por exemplo:

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

O dynamic_cast operador também pode ser usado para realizar um "cross cast". Usando a mesma hierarquia de classes, é possível converter um ponteiro, por exemplo, do subobjeto para o subobjeto, desde que o D objeto completo seja do B tipo E.

Considerando as transmissões cruzadas, é possível fazer a conversão de um ponteiro para um ponteiro para D o subobjeto mais A à esquerda em apenas duas etapas. Você pode executar uma conversão cruzada de D para B, e uma conversão implícita de B para A. Por exemplo:

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

Um valor de ponteiro nulo é convertido para um valor de ponteiro nulo do tipo de destino por dynamic_cast.

Quando você usa dynamic_cast < type-id > ( expression ), se expression não puder ser convertido com segurança em tipo type-id, a verificação em tempo de execução fará com que a conversão falhe. Por exemplo:

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

O valor de uma conversão falhada para o tipo de ponteiro é o ponteiro nulo. Uma conversão com falha do tipo de referência lança uma Exceção bad_cast. Se expression não apontar ou fazer referência a um objeto válido, uma __non_rtti_object exceção será lançada.

Confira typeid para obter uma explicação da exceção __non_rtti_object.

Exemplo

O exemplo a seguir cria o ponteiro (struct A) da classe base, para um objeto (struct C). Isso, além da existência de funções virtuais, permite a polimorfismo de runtime.

O exemplo também chama uma função não virtual na hierarquia.

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

Confira também

Operadores de conversão
Palavras-chave