dynamic_cast 運算子

將運算元 expression 轉換成 類型的 type-id 物件。

語法

dynamic_cast < type-id > ( expression )

備註

type-id必須是先前定義的類別型別的指標或參考,或是「void 的指標」。 如果 type-id 是指標,則 expression 類型必須是指標;或如果 type-id 是參考,則為左值 (l-value)。

如需靜態和動態轉換轉換之間的差異說明,以及何時適合使用每個轉換,請參閱 static_cast

Managed 程式碼的行為 dynamic_cast 有兩項重大變更:

  • dynamic_cast 至 Boxed 列舉基礎型別的指標在執行時間將會失敗,傳回 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
}

這種轉換稱為「向上轉換」,因為它會將指標向上移動類別階層,從衍生類別移至衍生自的類別。 向上轉換是隱含轉換。

如果 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* ,則會進行執行時間檢查,以查看 所指向的物件是否可以轉換成 所 type-id 指向 expression 的類型。

如果 的型 expression 別是 型 type-id 別的基類,則會進行執行時間檢查,以查看是否實際 expression 指向 型 type-id 別的完整物件。 如果這是 true,則結果會是 類型之完整物件的 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 指令,則會產生 no-op,如果轉換失敗,則會傳回 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 的基類。也是 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,而該類別繼承自類別 B,繼承自類別 A。

顯示虛擬基底類別的類別階層

在此階層中, A 是虛擬基類。 假設類別的 E 實例和子物件的指標 Adynamic_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 繼承,並透過類別 B 間接由類別 D 繼承,並透過類別 C 間接由類別 E 繼承,並間接透過類別 B 在類別 D 中繼承。

顯示重複基底類別的類別階層

假設類型為 的物件 E 和子物件的指標 D ,若要從 D 子物件巡覽至最 A 左邊的子物件,則可以進行三次轉換。 您可以執行 dynamic_cast 從指標到 E 指標的轉換,然後從 D 轉換 (或 dynamic_cast 隱含轉換) 轉換為 BE ,最後從 隱含轉換 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

考慮交叉轉換,只要兩個步驟,就可以在兩個步驟中,從指標轉換成 DA 左邊子物件的指標。 您可以執行從 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
}

Null 指標值會轉換成 目的地型 dynamic_cast 別的 Null 指標值。

當您使用 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
}

無法轉換成指標類型的值是 Null 指標。 無法轉換為參考型別的轉換會 擲回bad_cast例外狀況 。 如果未 expression 指向或參考有效的物件, __non_rtti_object 則會擲回例外狀況。

如需例外狀況的說明, __non_rtti_object 請參閱 typeid

範例

下列範例會建立物件 (結構 C) 的基類 (結構 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

另請參閱

轉型運算子
關鍵字