dynamic_cast 運算子
將運算元 expression 轉換至型別 type-id的物件。
dynamic_cast < type-id > ( expression )
備註
type-id 必須是指標或指向先前定義的類別的參考或是 「空指標」。 如果 type-id 是指標,expression 的型別必須為指標,或是如果 type-id 為參考,它就是 l-value。
請參閱 static_cast 以獲得靜態和動態轉型之間的差異,以及使用時機的說明。
Managed 程式碼在 dynamic_cast 上的行為有兩個重大變更 :
dynamic_cast 至指向基礎型別為 boxed enum 的指標會在執行階段失敗,如果是轉換後的指標則傳回 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 的完整物件。 如果為 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 指令,它執行動態檢查,如果轉換失敗傳回 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);
}
顯示多重繼承的類別階層架構
指向 D 型別物件的指標可以安全地轉換為 B 或 C。 不過,如果 D 被轉換為指向 A 物件, A 將會產生哪一個執行個體? 這樣會造成有歧義的轉換錯誤。 若要避過這個問題,您可以執行兩個明確的轉換。 例如:
// dynamic_cast_4.cpp
// compile with: /c /GR
class A {virtual void f();};
class B {virtual void f();};
class D : public B {virtual void f();};
void f() {
D* pd = new D;
B* pb = dynamic_cast<B*>(pd); // first cast to B
A* pa2 = dynamic_cast<A*>(pb); // ok: unambiguous
}
進一步的歧義會被使用虛擬基底類別時引入。 考慮下圖所示的類別階層架構。
顯示虛擬基底類別的類別階層架構
在階層架構中, A 是虛擬基底類別。 虛擬基底類別的定義請參閱 虛擬基底類別 。 給予類別為 E 的執行個體和指向 A 子物件的指標, dynamic_cast 至指標 B 會因為歧義而失敗。 您必須先轉換回完整的 E 物件,然後以明確的方法用您的方式往上移動階層架構,以達到正確的 B 物件。
考慮下圖所示的類別階層架構。
顯示重複基底類別的類別階層架構
給予型別為 E 的物件和指向 D 子物件的指標,從 D 子物件巡覽到最左邊的 A 子物件,三個轉換動作會被進行。 您可以執行從 D 指標用 dynamic_cast 轉換成 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 運算子也可以用來執行 「交互轉換」。使用相同類別階層架構,只要完整物件屬於型別 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 Exception。 如果 expression 沒有指向或沒有參考有效的物件,就會擲回 __non_rtti_object 例外狀況。
__non_rtti_object 例外狀況的說明請參閱 typeid 。
範例
下列範例建立指向物件 (結構 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);
// will fail because B knows nothing about C
B BonStack;
Globaltest(BonStack);
}