本文介紹c++的RTTI的基本用法,並初步研究RTTI的實現原理。
1. 什麼是RTTI
RTTI即運行時類型識別(runtime type identification),用於判斷指針或引用所綁定對象的動態類型,由兩個運算符實現:
2. 為什麼要用RTTI
當我們需要對象的類型信息時,比如需要使用非虛函數,有必要知道當前指針綁定的對象的動態類型。
3. 如何使用RTTI
作用於指針類型時,轉換失敗返回NULL;作用於引用時,因為引用不能為空,轉換失敗時返回std::bad_cast異常。
( printVal(){printf( derive: val, d):, *b = &*dp = dynamic<derive*>
typeid可以作用於任意表達式或類型,當運算對象不屬於類類型或不包含虛函數時,返回運算對象的靜態類型,否則typeid會直到運行時才決定其動態類型。typeid返回的值是type_info類型對象,type_info::name()返回類型的名字,當然這是編譯器相關的。一般用來判斷兩個對象動態類型是否相同或者某個對象是否是指定類型
printf(, typeid(derive).name());
6derive
4. 作用原理
那麼dynamic_cast和typeid是怎樣知道對象的實際類型的,在《inside the c++ object model》中反復提到類型信息是在虛函數表的第一個slot中的,不過這個跟編譯器實現出入很大,根據我的上篇文章可以發現gcc實現的虛函數表放的全是虛函數,並沒有類型信息。
derive d(, , &d);
一步步來debug,
derive d(10, 20.00);
(gdb) n
printf("deirve address: %x\n", &d);
(gdb)
deirve address: bffff2a0
(gdb) x /4a 0xbffff2a0
0xbffff2a0: 0xa 0x0 0x40340000
d的地址是0xbffff2a0, 第一個是虛函數表指針 0x80487e0,可以發現虛函數表在_ZTV6derive往後8個字節處,_ZTV6derive又是什麼?
(gdb) x /16a 0x80487d0
0x80487d0: 0x0 0x40340000 0x0
0x80487e0 <_ZTV6derive+8>: 0x80486ca <_ZN6derive8printValEv> 0x0 0x0 0x8048810 <_ZTI4base>
0x80487f0 <_ZTV4base+8>: 0x8048674 <_ZN4base8printValEv> 0x72656436 0x657669
0x8048800 <_ZTI6derive+4>: 0x80487f4 <_ZTS6derive> 0x8048810 <_ZTI4base> 0x73616234 0x65
可以發現ZTV6derive處的值為0,後面剛好是一個typeinfo的結構,指向 0x804a068,<_ZTVN10__cxxabiv120__si_class_type_infoE@@CXXABI_1.3+8>很明顯是一個class type info的對象。所以gcc在虛函數表前面多加了兩個字節,第一個為0,第二個指針指向了一個type info的結構。
至於這個type info結構到底是什麼樣的,我也沒找到資料,希望有知道的同學可以和我交流,謝謝。