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

Diagram that shows multiple inheritance.

该图显示了一个类层次结构,其中 A 是 B 的基类,而 B 是 D 的基类。A 也是 C 的基类,C 是 D 的基类。类 D 继承自 B 和 C。

指向 D 类型对象的指针可以安全地强制转换为 BC。 但是,如果 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 也继承自 D,D 继承自类 B,B 继承自类 A。

显示虚拟基类的类层次结构

在此层次结构中,A 是一个虚拟基类。 对于类 E 的实例和指向 A 子对象的指针,指向 B 的指针的 dynamic_cast 将因不明确性而失败。 必须首先转换回完整的 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 子对象,可以进行三次转换。 可以执行从 D 指针到 E 指针的 dynamic_cast 转换,然后执行从 EB 的转换(可以是 dynamic_cast,也可以是隐式转换),最后执行从 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 运算符还可用于执行“交叉强制转换”。使用同一类层次结构时,只要完整对象为 E 类型,就可以强制转换指针,例如从 B 子对象强制转换为 D 子对象。

就交叉强制转换而言,只需执行两个步骤即可完成从指向 D 的指针到指向最左侧 A 子对象的指针的转换。 可以执行从 DB 的交叉强制转换,然后从 BA 的隐式转换。 例如:

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

另请参阅

强制转换运算符
关键字