C++中typeid實現原理詳解
最近看了boost::any類原始碼,其實現主要依賴typeid操作符。很好奇這樣實現的時間和空間開銷有多大,決定探一下究竟。
VS2008附帶的type_info類只有標頭檔案,沒有原始檔,宣告如下:
class type_info { public: virtual ~type_info(); _CRTIMP_PURE bool __CLR_OR_THIS_CALL operator==(const type_info& rhs) const; _CRTIMP_PURE bool __CLR_OR_THIS_CALL operator!=(const type_info& rhs) const; _CRTIMP_PURE int __CLR_OR_THIS_CALL before(const type_info& rhs) const; _CRTIMP_PURE const char* __CLR_OR_THIS_CALL name(__type_info_node* __ptype_info_node = &__type_info_root_node) const; _CRTIMP_PURE const char* __CLR_OR_THIS_CALL raw_name() const; private: void *_m_data; char _m_d_name[1]; __CLR_OR_THIS_CALL type_info(const type_info& rhs); type_info& __CLR_OR_THIS_CALL operator=(const type_info& rhs); _CRTIMP_PURE static const char *__CLRCALL_OR_CDECL _Name_base(const type_info *,__type_info_node* __ptype_info_node); _CRTIMP_PURE static void __CLRCALL_OR_CDECL _Type_info_dtor(type_info *); };
測試程式碼:
#include <iostream> using namespace std; class Object { }; int main() { Object obj; cout << "type name:" << typeid(obj).name() << endl; cout << "type raw name:" << typeid(obj).raw_name() << endl; if(typeid(obj) == typeid(Object)) { cout << "type is equal" << endl; } else { cout << "type is not equal" << endl; } return 0; }
輸出:
type name:class Object
type raw name:.?AVObject@@
type is equal
在解釋每個函式的實現原理前先開看type_info類的儲存方式。
typeid返回的是type_info的引用,這個類不能拷貝,也不能自己構造,所以每個類最多隻有一個type_info的資料,這個資料存放在哪裡的呢?
用UltraEdit開啟exe檔案,搜尋“Object”,能找到這個字串。再用PE工具開啟這個exe,發現這個字串屬於data節(這是可讀可寫的全域性資料段)。再把有typeid的程式碼都註釋,PE檔案中沒有了這個字串。得出一個結論:
編譯器會為每一種typeid操作的型別生成一份儲存在資料段的type_info資料。
這份資料有多大呢?看下面這段程式碼:
#include <iostream> using namespace std; class Object { }; int main() { const type_info* p = &typeid(Object); cout << p << endl; return 0; }
在cout那一行下斷點,檢視到p的值為:
再看下這個類的宣告,解構函式為virtual型別的,所以p的頭四位元組為虛擬函式表。p+4為_m_data,void*型別,四個位元組,除錯時發現都是0,還不清楚其表示什麼。
p+8為_m_d_name,char型別陣列,儲存的是raw_name,每種型別的raw_name大小不定長,所以陣列長度為1。現在type_info的儲存結構已經一目瞭然:
每種型別的type_info資料長度依賴於型別名稱,至少9個位元組。
現在假設一個複雜的工程裡面有50個型別用了typeid操作符,平均每個type_info長度為24,這些資料增加的PE大小為1200B,就1K左右。而現在的PE動輒幾十M,所以這點空間開銷根本不算什麼。
再看看這些函式呼叫的開銷:
- raw_name函式直接返回_m_d_name的地址,非常快;
- name函式將_m_d_name儲存的字串解碼成實際的名稱,也是很快;
- ==操作符是比較raw_name是否相等,也是很快。
讀者可能會有兩點疑惑:
- 儲存的時候為什麼不直接儲存成name呢?我想最大的原因是節省空間,比如double的raw_name為".N",name為"double"多了四位元組。
- ==操作符為什麼不直接比較兩個type_info引用的地址是否相等呢?我也很疑惑,我看彙編碼發現它是比較raw_name。
備註:C++並沒有規定typeid實現標準,各個編譯器可能會不一樣,上述分析過程基於VS2008自帶的編譯器。
總結:typeid帶來的時間和空間開銷是非常小的,不過使用的時候儘量不要違背開放封閉原則。
到此這篇關於C++中typeid實現原理的文章就介紹到這了,更多相關C++ typeid實現原理內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!