1.什麼是RTTI?
RTTI 是“Runtime Type Information”的縮寫,意思是:運行時類型信息。它提供了運行時確定對象類型的方法。允許“用指向基類的指針或引用來操縱對象”的程序能夠獲取到“這些指針或引用所指對象”的實際派生類型。在 c++中,為了支持 RTTI 提供了兩個操作符 :
1 dynamic_cast 操作符:它允許在運行時刻進行類型轉換,從而使程序能夠在一個類層次結構中安全地轉換類型(安全的向下轉型)。把基類指針轉換成派生類指針,或把指向基類的左值轉換成派生類的引用。當然只有在保證轉換能夠成功的情況下才可以;
2 typeid操作符:它指出指針或引用指向的對象的實際派生類型。
但是,對於要獲得的派生類類型的信息,dynamic_cast 和 typeid操作符的操作數的類型必須是帶有一個或多個虛擬函數的類類型,即對於帶有虛擬函數的類而言,RTTI 操作符是運行時刻的事件,而對於其他類而言,它只是編譯時刻的事件。
C++的RTTI是最簡單的,只能獲得類名和相關的繼承信息;而VB、Delphi、Java等確復雜得多,甚至於支持屬性名、方法名、事件名等。
如圖,C++使用type_info類的對象保存每個對象的相關信息如對象名稱、對象類型等。而所謂RTTI就是在執行期取得對象的type_info。所以C++采用了和虛函數同樣的辦法,使用vtable(通常為第一個slot)保存需要執行期獲得type_info的對象的type_info對象地址。那麼由pt指向的class object的type_info可在執行期通過如下方式獲得:
(type_info)*(type_info*)(pt->vptr[0]);
通過上述實現分析我們也可以知道,C++的RTTI只能用於那些展現“多態”(內含虛函數)的類型有效。因為只有這樣的類才有vtable。
將一個Base class對象的指針轉為Derived class對象的指針或將Base class的左值轉為Derived class的引用稱為向下轉型。
注:不能講Base class的對象轉為Derived class的對象(除非定義相應轉換操作符)。如下語句是錯誤的:
Base bobj;
Derived dobj=static_cast<Derived>(bobj) ;//error,
Derived dobj=(Derived)(bobj) ;//error
但如下語句是正確的:
Derived dobj;
Base bobj=(Base)dobj;//正確,造成對象切割
Base bobj=static_cast<Base>(dobj);//正確,造成對象切割
向下轉型示例:
(1) 基類指針轉為子類指針
Base bobj;
Base *pb=&bobj;
Derived *pd=(Derived*)pb;//方式一
Derived *pd=static_cast<Derived*>(pb);//方式二
Derived *pd=dynamic_cast<Derived*>(pb);//方式三,只有Base class中有虛函數才能使用,否則編譯錯誤(Derived class中有虛函數也不行)
(2) 基類左值轉子類引用
Base bobj;
Derived dobj;
Derived &dref=(Derived&)(bobj);//方式一
Derived &dref=static_cast<Derived&>(bobj);//方式二
Derived &dref=dynamic_cast<Derived&>(bobj);//方式三,只有Base class中有虛函數才能使用,否則編譯錯誤(Derived class中有虛函數也不行)
(3) 基類左值轉子類引用
Derived dobj;
Base& bref=dobj;
Derived &dref=(Derived&)(bref);
Derived &dref=static_cast<Derived&>(bref);
Derived &dref=dynamic_cast<Derived&>(bref);
向下轉型的隱患
向下轉型有著潛在的危險,因為當基類指針指向的是基類對象,而將基類指針轉為子類指針時,如果通過轉換後的子類指針訪問子類的專有成員,就會造成內存錯誤,因為實際指向的是基類對象,而基類對象中不存在這些成員。(引用轉換類似)