在物件導向程式設計中,有時我們需要在運行時查詢一個對象是否能作為某種多態類型使用。與Java的instanceof,以及C#的as、is運算子類似,C++提供了dynamic_cast函數用於動態轉型。相比C風格的強制類型轉換和C++ reinterpret_cast,dynamic_cast提供了型別安全檢查,是一種基於能力查詢(Capability Query)的轉換,所以在多態類型間進行轉換更提倡採用dynamic_cast。本文主要介紹dynamic_cast的意義,用法和注意事項。
基本用法
dynamic_cast可以擷取目標對象的引用或指標:
T1 obj;
T2* pObj = dynamic_cast<T2*>(&obj);//轉換為T2指標,失敗返回NULL
T2& refObj = dynamic_cast<T2&>(obj);//轉換為T2引用,失敗拋出bad_cast異常
多態類型
在使用時需要注意:被轉換對象obj的類型T1必須是多態類型,即T1必須公有繼承自其它類,或者T1擁有虛函數(繼承或自訂)。若T1為非多態類型,使用dynamic_cast會報編譯錯誤。下面的例子說明了哪些類屬於多態類型,哪些類不是:
//A為非多態類型
class A{
};
//B為多態類型
class B{
public: virtual ~B(){}
};
//D為多態類型
class D: public A{
};
//E為非多態類型
class E : private A{
};
//F為多態類型
class F : private B{
}
橫向轉型
在多態類型間轉換,分為3種類型:
1.子類向基類的向上轉型(Up Cast)
2.基類向子類的向下轉型(Down Cast)
3.橫向轉型(Cross Cast)
向上轉型是多態的基礎,需不要藉助任何特殊的方法,只需用將子類的指標或引用賦給基類的指標或引用即可,當然dynamic_cast也支援向上轉型,而其總是肯定成功的。而對於向下轉型和橫向轉型來講,其實對於dynamic_cast並沒有任何區別,它們都屬於能力查詢。為了理解方便,我們不妨把dynamic_cast視為cross cast:
class Shape {
public: virtual ~Shape();
virtual void draw() const = 0;
};
class Rollable {
public: virtual ~Rollable();
virtual void roll() = 0;
};
class Circle : public Shape, public Rollable {
void draw() const;
void roll();
};
class Square : public Shape {
void draw() const;
};
//橫向轉型失敗
Shape *pShape1 = new Square();
Rollable *pRollable1 = dynamic_cast<Rollable*>(pShape2);//pRollable為NULL
//橫向轉型成功
Shape *pShape2 = new Circle();
Rollable *pRollable2 = dynamic_cast<Rollable*>(pShape2);//pRollable不為NULL
指標比較
接上面的例子,在我的機器上pShape2和pRollable2的值(所指向的地址)分別為:
pShape2: 0x0039A294, pRollable2:0x0039A290
說明dynamic_cast在進行轉型的時候對不同多態類型設定了不同的位移量。接下來的問題是
pRollable2 == pShape2
這個運算式應該返回什麼呢?答案是:1,即指標比較相等。也許從C語言轉到C++的朋友可能會感到困惑,因為在C語言中指標的比較只是值比較而已。顯然,對於多態類型,C++編譯器為==運算子做了更多的幕後工作來保證指標比較注重對象的同一性而非指標的值。至於實現細節涉及到C++物件模型,這是我還不甚熟悉的內容,故本文不再深入。
參考
C++ Common Knowledge Item27,28
C++ Typecasting
文中若有錯誤和不足,歡迎各位批評指正!