通過RTTI,能夠通過基類的指針或引用來檢索其所指對象的實際類型。c++通過下面兩個操作符提供RTTI。
(1)typeid:返回指針或引用所指對象的實際類型。
(2)dynamic_cast:將基類類型的指針或引用安全的轉換為派生類型的指針或引用。
對於帶虛函數的類,在運行時執行RTTI操作符,返回動態類型信息;對於其他類型,在編譯時執行RTTI,返回靜態類型信息。
當具有基類的指針或引用,但需要執行派生類操作時,需要動態的強制類型轉換(dynamic_cast)。這種機制的使用容易出錯,最好以虛函數機制代替之。
dynamic_cast 操作符
如果dynamic_cast轉換指針類型失敗,則返回0;如果轉換引用類型失敗,則拋出一個bad_cast類型的異常。
可以對值為0的指針使用dynamic_cast,結果為0。
dynamic_cast會首先驗證轉換是否有效,只有轉換有效,操作符才進行實際的轉換。
if (Derived *derivedPtr = dynamic_cast<Derived *>(basePtr)) { // use the Derived object to which derivedPtr points } else { // basePtr points at a Base object // use the Base object to which basePtr points }
也可以使用dynamic_cast將基類引用轉換為派生類引用:dynamic_cast<Type&>(val)
因為不存在空引用,所以不能像指針一樣對轉換結果進行判斷。不過轉換引用類型失敗時,會拋出std::bad_cast異常。
try { const Derived &d = dynamic_cast<const Derived&>(b); } catch (bad_cast) { // handle the fact that the cast failed. }
typeid操作符
typeid能夠獲取一個表達式的類型:typeid(e)。
如果操作數不是類類型或者是沒有虛函數的類,則獲取其靜態類型;如果操作數是定義了虛函數的類類型,則計算運行時類型。
typeid最常見的用途是比較兩個表達式的類型,或者將表達式的類型與特定類型相比較。
Base *bp; Derived *dp; // compare type at run time of two objects if (typeid(*bp) == typeid(*dp)) { // bp and dp point to objects of the same type } // test whether run time type is a specific type if (typeid(*bp) == typeid(Derived)) { // bp actually points a Derived }
注意:如果是typeid(bp),則是對指針進行測試,這會返回指針(bp)的靜態編譯時類型(Base *)。
如果指針p的值是0,,並且指針所指的類型是帶虛函數的類型,則typeid(*p)拋出一個bad_typeid異常。
RTTI的使用
如果有一個類層次,希望為它實現“==”操作符。假設類層次中只有2個類型,那么需要4個函數:
bool operator==(const Base&, const Base&) bool operator==(const Derived&, const Derived &) bool operator==(const Derived &, const Base&) bool operator==(const Base&, const Derived &)
如果類層次中有4個類型,就要實現16個操作符函數,這種實現就太麻煩了。下面來看如何使用RTTI解決這個問題。
只定義1個“==”操作符函數,每個類定義一個虛函數equal。
class Base { friend bool operator==(const Base&, const Base&); public: // interface members for Base protected: virtual bool equal(const Base&) const; // data and other implementation members of Base }; bool Base::equal(const Base &rhs) const { // do whatever is required to compare to Base objects } class Derived: public Base { friend bool operator==(const Base&, const Base&); public: // other interface members for Derived private: bool equal(const Base&) const; // data and other implementation members of Derived }; bool Derived::equal(const Base &rhs) const { if (const Derived *dp = dynamic_cast<const Derived *>(&rhs)) { // do work to compare two Derived objects and return result } else return false; } bool operator==(const Base &lhs, const Base &rhs) { // returns false if typeids are different otherwise // returns lhs.equal(rhs) return typeid(lhs) == typeid(rhs) && lhs.equal(rhs); }
如果操作數類型不同,操作符就返回假;如果操作數類型相同,就將實際比較操作數的工作委派給適當的虛函數equal。
Derived::equal()中的dynamic_cast強制轉換是必要的。因為要比較派生類的成員,必須將操作數Base &轉換為Derived類型。
type_info類
type_info類的實現因編譯器的不同而不同。但如下幾個常用的操作符和函數是c++標准要求必須實現的:“t1 == t2”、“t1 != t2”、“t.name()”。
typeid操作符的返回類型就是type_info,正因為type_info提供了“==”操作符,才可以進行上面提到的“if (typeid(*bp) == typeid(*dp))”判斷。
type_info的默認構造函數、拷貝構造函數、賦值操作符都定義為private,創建type_info對象的唯一方法就是使用typeid操作符。
name()函數返回類型名字的c-style字符串,但字符串的格式可能不同的編譯器略有不同。下面是在vc2008編譯器下的測試。
// expre_typeid_Operator.cpp // compile with: /GR /EHsc #include <iostream> #include <typeinfo.h> class Base { public: virtual void vvfunc() {} }; class Derived : public Base {}; using namespace std; int main() { Derived* pd = new Derived; Base* pb = pd; int i = 0; cout << typeid( i ).name() << endl; // prints "int" cout << typeid( 3.14 ).name() << endl; // prints "double" cout << typeid( pb ).name() << endl; // prints "class Base *" cout << typeid( *pb ).name() << endl; // prints "class Derived" cout << typeid( pd ).name() << endl; // prints "class Derived *" cout << typeid( *pd ).name() << endl; // prints "class Derived" delete pd; }
【學習資料】 《c++ primer》