dynamic_cast 运算符
将操作数 expression
转换为 type-id
类型的对象。
语法
dynamic_cast < type-id > ( expression )
备注
type-id
必须是针对以前定义的类类型的指针或引用,或者是“指向 void 的指针”。 如果 type-id
是指针,则 expression
类型必须为指针,或者如果 type-id
是引用,则为左值。
请参阅 static_cast,了解静态和动态强制转换之间的区别,以及什么情况下适合使用这两种转换。
托管代码中的 dynamic_cast
行为有两项中断性变更:
对指向装箱枚举的基础类型的指针的
dynamic_cast
将在运行时失败,返回 0 而不是转换后的指针。当
type-id
是指向值类型的内部指针时,dynamic_cast
将不再引发异常;相反,强制转换在运行时失败。 强制转换返回 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
}
这种类型的转换称为“向上转换”,因为它将指针向上移动一个类层次结构,从派生类移至它从中派生的类。 向上转换是一种隐式转换。
如果 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
}
这种类型的转换称为“向下转换”,因为它将指针向下移动一个类层次结构,从给定类移至从中派生的类。
如果有多重继承,可能会导致不明确。 来看看下图中显示的类层次结构。
对于 CLR 类型,如果可以隐式执行转换,则 dynamic_cast
会导致无操作,或者执行 MSIL isinst
指令,该指令执行动态检查并在转换失败时返回 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 的基类,而 B 是 D 的基类。A 也是 C 的基类,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 也继承自 D,D 继承自类 B,B 继承自类 A。
显示虚拟基类的类层次结构
在此层次结构中,A
是一个虚拟基类。 对于类 E
的实例和指向 A
子对象的指针,指向 B
的指针的 dynamic_cast
将因不明确性而失败。 必须首先转换回完整的 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
子对象,可以进行三次转换。 可以执行从 D
指针到 E
指针的 dynamic_cast
转换,然后执行从 E
到 B
的转换(可以是 dynamic_cast
,也可以是隐式转换),最后执行从 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
运算符还可用于执行“交叉强制转换”。使用同一类层次结构时,只要完整对象为 E
类型,就可以强制转换指针,例如从 B
子对象强制转换为 D
子对象。
就交叉强制转换而言,只需执行两个步骤即可完成从指向 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
}
一个空指针值通过 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
异常的说明,请参阅 typeid。
示例
下面的示例创建了基类(结构 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 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈