RTTI
運行時類型識別(RTTI)的引入有三個作用:
- 配合typeid操作符的實現;
- 實現異常處理中catch的匹配過程;
- 實現動態類型轉換dynamic_cast
typeid操作符的實現
靜態類型
C++中支持使用typeid關鍵字獲取對象類型信息,它的返回值類型是const std::type_info&,例
#include <typeinfo>
#include <cassert>
struct B {} b, c;
struct D : B {} d;
void test() {
const std::type_info& tb = typeid(b);
const std::type_info& tc = typeid(c);
const std::type_info& td = typeid(d);
assert(tb == tc); // b和c具有相同的類型
assert(&tb == &tc); // tb和tc引用的是相同的對象
assert(tb != td); // 雖然D是B的子類,但是b和d的類型卻不同
assert(&tb != &td); // tb和td引用的是不同的對象
}
動態類型
當typeid的操作數引用的是一個動態類(含有虛函數的類) 類型時,它的返回值是被引用對象對應類型的const std::type_info&,例:
#include <typeinfo>
#include <cassert>
struct B { virtual void foo() {} };
struct C { virtual void bar() {} };
struct D : B, C {};
void test() {
D d;
B& rb = d;
C& rc = d;
assert(typeid(rb) == typeid(d)); // rb引用的類型與d相同
assert(typeid(rb) == typeid(rc)); // rb引用的類型與rc引用的類型相同
}
編譯時可能還不知道rb或rc引用的類型,運行時怎么能判斷該返回基類還是派生類對應的類型信息對象呢?
首先我們看看虛表的結構,在虛函數指針前面有RTTI信息。
如果是含有虛函數,運行時便可以通過vptr找到“虛函數表”,而“虛函數表”之前的一個位置存放了需要的類型信息對象,typeid可以直接返回這里的類型信息對象引用即可。
實現異常處理中catch的匹配過程
catch的匹配過程也可利用與typeid相似的原理進行類型匹配判斷,此不再贅述。
動態類型轉換(dynamic_cast)
先上一個例子:
type_info對象里面會有整個繼承體系的信息(通過指針),因此繼承關系可以通過此樹狀結構判斷,有了繼承關系,再遞歸從虛表中查找基類子對象在派生類中的偏移值,便可以確定最終返回地址。
- '# 4例子:
通過pa找到_D的type_info,對比和C的type_info的不同,但是在type_info的指針里面找到C的type_info。加上C的type_info的通過偏移量,返回指針結果 - '# 2例子:
通過pa找到D的type_info,和目標D的type_info相同,直接加上偏移量,返回指針