Оператор 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);
}
На схеме показана иерархия классов с 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
}
При использовании виртуальных базовых классов могут возникать дополнительные неоднозначности. Рассмотрим для примера иерархию классов, показанную на следующем рисунке.
На схеме показаны классы A, B, C, D и E, упорядоченные следующим образом: Класс A является базовым классом B. КлассЫ C и E, производные от B. Класс E также наследуется от класса B, который наследует от класса A.
Иерархия классов, показывающая виртуальные базовые классы
В этой иерархии объект A
является виртуальным базовым классом. Учитывая экземпляр класса E
и указатель на A
подобъект, dynamic_cast
указатель на указатель B
на ошибку из-за неоднозначности. Необходимо сначала выполнить обратное приведение к полному объекту E
, затем однозначным образом вернуться вверх по иерархии, чтобы дойти до нужного объекта B
.
Рассмотрим для примера иерархию классов, показанную на следующем рисунке.
На схеме показаны классы 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
неявное преобразование) в E
B
, а затем неявное преобразование из B
A
. Например:
// 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
См. также
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по