天天看點

c++ RTTI(運作時類型識别)

通過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》