Оператор dynamic_cast

Преобразует операнд expression в объект типа type-id.

Синтаксис

dynamic_cast < type-id > ( expression )

Замечания

Параметр type-id должен быть указателем или ссылкой на ранее определенный тип класса или "указателем на void". Тип операнда expression должен быть указателем, если type-id является указателем, или l-значением, если type-id является ссылкой.

Ознакомьтесь static_cast с объяснением разницы между статическими и динамическими преобразованиями приведения, а также при необходимости их использования.

В управляемом dynamic_cast коде есть два критических изменения:

  • dynamic_cast Указатель на базовый тип прямоугольной перечисления завершится ошибкой во время выполнения, возвращая значение 0 вместо преобразованного указателя.

  • dynamic_cast Больше не вызывает исключение, если type-id это внутренний указатель на тип значения. Вместо этого приведение завершается ошибкой во время выполнения. Приведение возвращает значение указателя 0 вместо того, чтобы вызывать.

Если type-id это указатель на однозначно доступный прямой или косвенный базовый класс expression, то указатель на уникальный подобъект типа type-id является результатом. Например:

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

Этот тип преобразования называется "upcast", так как он перемещает указатель вверх по иерархии классов, от производного класса к классу, который он является производным. Восходящее приведение типа является неявным преобразованием.

Если type-id является указателем void*, выполняется проверка во время выполнения, чтобы определить фактический тип операнда expression. Результатом является указатель на полный объект, на который указывает операнд expression. Например:

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

Если type-id это не void*так, выполняется проверка времени выполнения, чтобы узнать, может ли объект, на который указывает объектexpression, можно преобразовать в тип, на который указывает.type-id

Если тип операнда expression является базовым классом типа, заданного параметром type-id, то выполняется проверка во время выполнения, чтобы определить, указывает ли операнд expression на полный объект типа, заданного параметром type-id. Если это так, то результатом является указатель на полный объект типа, заданного параметром type-id. Например:

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

Этот тип преобразования называется "нисходящим приведением типа", поскольку при нем указатель перемещается вниз по иерархии классов: от заданного класса к производному от него классу.

В случаях множественного наследования возникают возможности для неоднозначности. Рассмотрим для примера иерархию классов, показанную на следующем рисунке.

Если преобразование может выполняться неявно, либо инструкция MSILisinst, dynamic_cast которая выполняет динамический проверка и возвращаетсяnullptr, если преобразование завершается ошибкой.

Следующий пример используется dynamic_cast для определения того, является ли класс экземпляром определенного типа:

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

На схеме показана иерархия классов с A в качестве базового класса B, который является базовым классом D. A также является базовым классом для C, который является базовым классом для D. Класс D наследует как от B, так и от C.

Указатель на объект типа D можно безопасно привести к B или C. Однако если в результате приведения D указывает на объект A, какой экземпляр объекта A будет являться результатом? Это может привести к ошибке неоднозначного приведения. Чтобы обойти эту проблему, можно выполнить два однозначных приведения. Например:

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

При использовании виртуальных базовых классов могут возникать дополнительные неоднозначности. Рассмотрим для примера иерархию классов, показанную на следующем рисунке.

Diagram of a class hierarchy that shows virtual base classes.

На схеме показаны классы A, B, C, D и E, упорядоченные следующим образом: Класс A является базовым классом B. КлассЫ C и E, производные от B. Класс E также наследуется от класса B, который наследует от класса A.

Иерархия классов, показывающая виртуальные базовые классы

В этой иерархии объект A является виртуальным базовым классом. Учитывая экземпляр класса E и указатель на A подобъект, dynamic_cast указатель на указатель B на ошибку из-за неоднозначности. Необходимо сначала выполнить обратное приведение к полному объекту E, затем однозначным образом вернуться вверх по иерархии, чтобы дойти до нужного объекта B.

Рассмотрим для примера иерархию классов, показанную на следующем рисунке.

Diagram of a class hierarchy that shows duplicate base classes.

На схеме показаны классы A, B, C, D и E, упорядоченные следующим образом: класс B является производным от класса A. Класс C является производным от класса A. Класс D является производным от класса B. Класс E является производным от класса C, который является производным от класса A. В этом случае повторяющийся базовый класс — это класс A, который прямо или косвенно наследуется всеми другими классами. Класс A наследуется непосредственно классами B и C, а также косвенно по классу D через класс B и косвенно по классу E через класс C и косвенно в классе D через класс B.

Иерархия классов, показывающая повторяющиеся базовые классы

При использовании заданного объекта типа E и указателя на подобъект D можно выполнить три преобразования, чтобы перейти от подобъекта D к крайнему слева подобъекту A. Вы можете выполнить dynamic_cast преобразование из D указателя E в указатель, а затем преобразование (либо dynamic_cast неявное преобразование) в EB, а затем неявное преобразование из BA. Например:

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

Оператор dynamic_cast также можно использовать для выполнения перекрестного приведения. Используя ту же иерархию классов, можно привести указатель, например, из B подобъекта в D подобъект, если полный объект имеет тип E.

Учитывая перекрестные приведения, можно выполнить преобразование с указателя на указатель D на A левый элемент подобъекта всего за два шага. Можно перекрестное приведение из D в B, а затем неявное преобразование из B в A. Например:

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

Значение указателя null преобразуется в значение null указателя целевого типа по dynamic_cast.

При использовании dynamic_cast < type-id > ( expression ), если expression невозможно безопасно преобразовать в типtype-id, время выполнения проверка приводит к сбою приведения. Например:

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

Значением приведения к типу указателя, которое привело к сбою, является пустой указатель. Сбой приведения к типу ссылки вызывает исключение bad_cast. Если expression не указывает на допустимый объект или не ссылается на нее, __non_rtti_object создается исключение.

Сведения об исключении см. в описании __non_rtti_object типа.

Пример

В следующем примере создается указатель базового класса (структура A) на объект (структура C). Это (а также тот факт, что они являются виртуальными функциями) порождает полиморфизм времени выполнения.

В примере также вызывается невиртуальная функция в иерархии.

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

См. также

Операторы приведения
Ключевые слова