C++運行時類型識別


通過運行時類型識別(RTTI),程序能夠使用基類的指針或引用來檢索這些指針或引用所指對象的實際派生類型。

通過下面兩個操作符提供 RTTI:

1. typeid 操作符,返回指針或引用所指對象的實際類型。

2. dynamic_cast 操作符,將基類類型的指針或引用安全地轉換為派生類型的指針或引用。

注意,這些操作符只為帶有一個或多個虛函數的類返回動態類型信息,對於其他類型,返回靜態(即編譯時)類型的信息。對於帶虛函數的類,在運行時執行 RTTI 操作符,但對於其他類型,在編譯時計算 RTTI 操作符。

 

1:dynamic_cast 操作符

可以使用 dynamic_cast 操作符將基類類型對象的引用或指針轉換為同一繼承層次中其他類型的引用或指針。與 dynamic_cast 一起使用的指針必須是有效的——它必須為 0 或者指向一個對象。

dynamic_cast 涉及運行時類型檢查。如果綁定到引用或指針的對象不是目標類型的對象,則 dynamic_cast 失敗。如果轉換到指針類型的 dynamic_cast 失敗,則 dynamic_cast 的結果是 0 值;如果轉換到引用類型的 dynamic_cast 失敗,則拋出一個 bad_cast 類型的異常。

可以對值為 0 的指針應用 dynamic_cast,這樣做的結果是 0。

 

假定 Base 是至少帶一個虛函數的類,並且 Derived 類派生於Base 類。如果有一個名為 basePtr 的指向 Base 的指針,就可以像這樣在運行時將它強制轉換為指向 Derived 的指針:

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 操作的形式如下: dynamic_cast< Type& >(val)

只有當 val 實際引用一個 Type 類型對象,或者 val 是一個 Type 派生類型的對象的時候,dynamic_cast 操作才將操作數 val 轉換為想要的 Type& 類型。當轉換失敗的時候,它拋出一個 std::bad_cast 異常,該異常在庫頭文件 typeinfo 中定義。

重寫前面的例子如下:

void f(const Base &b)
{
try {
const Derived &d = dynamic_cast<const Derived&>(b);
// use the Derived object to which b referred
} catch (bad_cast) {
// handle the fact that the cast failed
}
}

 

 

2:typeid 操作符

typeid 操作符使程序能夠返回一個表達式的類型。typeid 表達式形如:typeid(e) 。這里 e 是任意表達式或者是類型名。

typeid 操作符的結果是名為 type_info 的標准庫類型的對象引用。

如果表達式的類型是類類型且該類包含一個或多個虛函數,則表達式的動態類型可能不同於它的靜態編譯時類型。例如,如果表達式對基類指針解引用,則該表達式的靜態編譯時類型是基類類型;但是,如果指針實際指向派生類對象,則 typeid 操作符將說表達式的類型是派生類型。

typeid 操作符可以與任何類型的表達式一起使用。內置類型的表達式以及常量都可以用作 typeid 操作符的操作數。如果操作數不是類類型或者是沒有虛函數的類,則 typeid 操作符指出操作數的靜態類型;如果操作數是定義了至少一個虛函數的類類型,則在運行時計算類型。

 

typeid 最常見的用途是比較兩個表達式的類型,或者將表達式的類型與特定類型相比較:

Base *bp;
Derived *dp;
if (typeid(*bp) == typeid(*dp)) {
// bp and dp point to objects of the same type
}
if (typeid(*bp) == typeid(Derived)) {
// bp actually points to a Derived
}

 

注意,typeid 的操作數是表示對象的表達式——測試 *bp,而不是 bp。

 

如果指針 p 的值是 0,那么,如果 p 的類型是帶虛函數的類型,則typeid(*p) 拋出一個 bad_typeid 異常;如果 p 的類型沒有定義任何虛函數,則結果與 p 的值是不相關的。正像計算表達式 sizeof一樣,編譯器不計算 *p,它使用 p 的靜態類型,這並不要求 p 本身是有效指針。

 

3:type_info 類

type_info 類的確切定義隨編譯器而變化,但是,標准保證所有的實現將至少提供下表 列出的操作。

t1 == t2

 如果兩個對象 t1 和 t2 類型相同,就返回 true;否則,返回false

t1 != t2

 如果兩個對象 t1 和 t2 類型不同,就返回 true;否則,返回false

t.name()

返回 C 風格字符串,這是類型名字的可顯示版本。類型名字用系統相關的方法產生

t1.before(t2)

返回指出 t1 是否出現在 t2 之前的 bool 值。before 強制的次序與編譯器有關

默認構造函數和復制構造函數以及賦值操作符都定義為 private,所以不能定義或復制 type_info 類型的對象。程序中創建 type_info 對象的唯一方法是使用 typeid 操作符。

 

name 函數為 type_info 對象所表示的類型的名字返回 C 風格字符串。給定類型所用的值取決於編譯器:

int iobj;
cout << typeid(iobj).name() << endl
<< typeid(8.16).name() << endl
<< typeid(std::string).name() << endl
<< typeid(Base).name() << endl
<< typeid(Derived).name() << endl;

 

name 返回的格式和值隨編譯器而變化。在我們的機器上執行時,這個程序產生下面的輸出:

i
d
Ss
4Base
7Derived

 

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: