Compartir a través de


dynamic_cast (Operador)

Convierte el operando expression en un objeto del tipo type-id.

Sintaxis

dynamic_cast < type-id > ( expression )

Comentarios

type-id debe ser un puntero o una referencia a un tipo de clase definido previamente o un "puntero a void". El tipo de expression debe ser un puntero si type-id es un puntero, o un valor L si type-id es una referencia.

Consulte static_cast para obtener una explicación de la diferencia entre las conversiones de conversión estática y dinámica, y cuándo es adecuado usar cada una.

Hay dos cambios de ruptura en el comportamiento del dynamic_cast en el código administrado:

  • dynamic_cast a un puntero al tipo subyacente de una enumeración boxed se producirá un error en tiempo de ejecución, devolviendo 0 en lugar del puntero convertido.

  • dynamic_cast ya no producirá una excepción cuando type-id es un puntero interior a un tipo de valor; en su lugar, se produce un error en la conversión en tiempo de ejecución. La conversión devuelve el valor de puntero 0 en lugar de iniciarse.

Si type-id es un puntero a una clase base directa o indirecta accesible sin ambigüedades de expression, un puntero al subobjeto único de tipo type-id es el resultado. Por ejemplo:

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

Este tipo de conversión se denomina "upcast" porque mueve un puntero a una jerarquía de clases, de una clase derivada a una clase de la que se deriva. Una conversión hacia arriba es una conversión implícita.

Si type-id es void*, se realiza una comprobación en tiempo de ejecución para determinar el tipo real de expression. El resultado es un puntero al objeto completo al que apunta expression. Por ejemplo:

// 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 no void*es , se realiza una comprobación en tiempo de ejecución para ver si el objeto al expression que apunta se puede convertir en el tipo al que apunta type-id.

Si el tipo de expression es una clase base del tipo de type-id, se realiza una comprobación en tiempo de ejecución para ver si expression apunta realmente a un objeto completo del tipo de type-id. Si es cierto, el resultado es un puntero a un objeto completo del tipo de type-id. Por ejemplo:

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

Este tipo de conversión se denomina "conversión hacia abajo" porque baja un puntero en una jerarquía de clases, desde una clase especificada a una clase derivada de ella.

En los casos de herencia múltiple, se introducen posibilidades de ambigüedad. Considere la jerarquía de clases que se muestra en la ilustración siguiente.

Para los tipos CLR, dynamic_cast el resultado es un no-op si la conversión puede realizarse implícitamente, o una instrucción MSIL isinst, que realiza una comprobación dinámica y devuelve nullptr si la conversión falla.

El siguiente ejemplo utiliza dynamic_cast para determinar si una clase es una instancia de un tipo particular:

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

Diagrama que muestra varias herencias.

El diagrama muestra una jerarquía de clases con A como una clase base de B, que es una clase base de D. A también es una clase base para C, que es una clase base para D. Clase D hereda de B y C.

Un puntero a un objeto de tipo D se puede convertir de manera segura a B o a C. Sin embargo, si D se convierte para que apunte a un objeto A, ¿qué instancia de A resultaría? Esto produciría un error de conversión ambigua. Para eludir este problema, puede realizar dos conversiones no ambiguas. Por ejemplo:

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

Se pueden introducir más ambigüedades cuando se utilizan clases base virtuales. Considere la jerarquía de clases que se muestra en la ilustración siguiente.

Diagrama de una jerarquía de clases que muestra clases base virtuales.

En el diagrama se muestran las clases A, B, C, D y E organizadas de la siguiente manera: La clase A es una clase base de B. Clases C y E que derivan de B. La clase E también hereda de D, que hereda de la clase B, que hereda de la clase A.

Jerarquía de clases que muestra clases base virtuales

En esta jerarquía, A es una clase base virtual. Dada una instancia de clase E y un puntero al A subobjeto, se produce un error en dynamic_cast un puntero a un puntero debido B a la ambigüedad. Primero debe convertir de nuevo al objeto E completo y después volver a subir por la jerarquía, de forma no ambigua, hasta llegar al objeto B correcto.

Considere la jerarquía de clases que se muestra en la ilustración siguiente.

Diagrama de una jerarquía de clases que muestra clases base duplicadas.

En el diagrama se muestran las clases A, B, C, D y E organizadas de la siguiente manera: La clase B deriva de la clase A. La clase C deriva de la clase A. la clase D deriva de la clase B. La clase E deriva de la clase C, que deriva de la clase A. En este caso, la clase base duplicada es la clase A, que se hereda directa o indirectamente por todas las demás clases. La clase A se hereda directamente por las clases B y C, e indirectamente por la clase D a través de la clase B, e indirectamente por la clase E a través de la clase C, e indirectamente en la clase D a través de la clase B.

Jerarquía de clases que muestra clases base duplicadas

Dado un objeto de tipo E y un puntero al subobjeto D, para navegar desde el subobjeto D hasta el subobjeto A situado más a la izquierda, se pueden realizar tres conversiones. Se puede realizar una conversión dynamic_cast del puntero D a un puntero E, luego una conversión (cualquier dynamic_cast o una conversión implícita) de E a B, y finalmente una conversión implícita de B a A. Por ejemplo:

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

El dynamic_cast operador también se puede usar para realizar una "conversión cruzada". Con la misma jerarquía de clases, es posible convertir un puntero, por ejemplo, desde el B subobjeto al D subobjeto, siempre y cuando el objeto completo sea de tipo E.

Teniendo en cuenta las conversiones cruzadas, es posible realizar la conversión desde un puntero a D un puntero al subobjeto izquierdo en A solo dos pasos. Puede realizar una conversión cruzada de D a B y después una conversión implícita de B a A. Por ejemplo:

// 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 valor de puntero nulo se convierte en el valor de puntero nulo del tipo de destino mediante dynamic_cast.

Cuando se usa dynamic_cast < type-id > ( expression ), si expression no se puede convertir de forma segura al tipo type-id, la comprobación en tiempo de ejecución hace que se produzca un error en la conversión. Por ejemplo:

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

El valor de una conversión con errores al tipo de puntero es el puntero NULL. Una conversión fallida a un tipo de referencia lanza una Excepción bad_cast. Si expression no apunta o hace referencia a un objeto válido, se produce una __non_rtti_object excepción.

Consulte typeid para una explicación de la excepción __non_rtti_object.

Ejemplo

En el ejemplo siguiente se crea el puntero de la clase base (struct A) a un objeto (struct C). Esto, además del hecho de que hay funciones virtuales, hace posible el polimorfismo en tiempo de ejecución.

El ejemplo también llama a una función no virtual en la jerarquía.

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

Consulte también

Operadores de conversión
Palabras clave