typeid關鍵字
注意:typeid是操作符,不是函數。這點與sizeof類似)
運行時獲知變量類型名稱,可以使用 typeid(變量).name()
需要注意不是所有編譯器都輸出”int”、”float”等之類的名稱,對於這類的編譯器可以這樣使用
1 int ia = 3; 2 if(typeid(ia) == typeid(int)) 3 { 4 cout <<"int" <<endl; 5 }
RTTI(Run-Time Type Identification)-運行時類型識別
在揭開typeid神秘面紗之前,我們先來了解一下RTTI(Run-Time Type Identification,運行時類型識別),它使程序能夠獲取由基指針或引用所指向的對象的實際派生類型,即允許“用指向基類的指針或引用來操作對象”的程序能夠獲取到“這些指針或引用所指對象”的實際派生類型。
在C++中,為了支持RTTI提供了兩個操作符:dynamic_cast
和typeid
-
dynamic_cast允許運行時刻進行類型轉換,從而使程序能夠在一個類層次結構中安全地轉化類型,與之相對應的還有一個非安全的轉換操作符static_cast,因為這不是本文的討論重點,所以這里不再詳述,感興趣的可以自行查閱資料。
-
typeid是C++的關鍵字之一,等同於sizeof這類的操作符。typeid操作符的返回結果是名為type_info的標准庫類型的對象的引用(在頭文件typeinfo中定義,稍后我們看一下vs和gcc庫里面的源碼),它的表達式有下圖兩種形式。
實現機制與使用技巧
type_info類對象類別判別
對象類別判別分析
如果表達式的類型是類類型且至少包含有一個虛函數,則typeid操作符返回表達式的動態類型,需要在運行時計算;
否則,typeid操作符返回表達式的靜態類型,在編譯時就可以計算。
ISO C++標准並沒有確切定義type_info
,它的確切定義編譯器相關的,但是標准卻規定了其實現必需提供如下四種操作(在之后的章節中我會來分析type_info類文件的源碼)
運算 | 描述 |
---|---|
t1 == t2 | 如果兩個對象t1和t2類型相同,則返回true;否則返回false |
t1 != t2 | 如果兩個對象t1和t2類型不同,則返回true;否則返回false |
t.name() | 返回類型的C-style字符串,類型名字用系統相關的方法產生1 |
t1.before(t2) | 返回指出t1是否出現在t2之前的bool值 |
type_info類提供了public虛 析構函數,以使用戶能夠用其作為基類。它的默認構造函數和拷貝構造函數及賦值操作符都定義為private,所以不能定義或復制type_info類型的對象。程序中創建type_info對象的唯一方法是使用typeid操作符(由此可見,如果把typeid看作函數的話,其應該是type_info的 友元)。type_info的name成員函數返回C-style的字符串,用來表示相應的類型名,但務必注意這個返回的類型名與程序中使用的相應類型名並不一定一致(往往如此,見后面的程序),這具體由編譯器的實現所決定的,標准只要求實現為每個類型返回唯一的字符串。
type_info類源代碼
使用sudo find / -name typeinfo.h
來查找源碼
1 #ifndef _TYPEINFO 2 #define _TYPEINFO
3
4 #include <exception>
5
6 namespace std 7 { 8
9 class type_info 10 { 11 public: 12
13 virtual ~type_info(); 14 { return __name[0] == '*' ? __name + 1 : __name; } 15
16
17 bool before(const type_info& __arg) const
18 { return __name < __arg.__name; } 19
20 bool operator==(const type_info& __arg) const
21 { return __name == __arg.__name; } 22
23 bool operator!=(const type_info& __arg) const
24 { return !operator==(__arg); } 25
26 virtual bool __is_pointer_p() const; 27
28 virtual bool __is_function_p() const; 29
30 protected: 31 const char *__name; 32
33 explicit type_info(const char *__n): __name(__n) { } 34
35 private: 36 type_info& operator=(const type_info&); 37 type_info(const type_info&); 38 }; 39
40 } // extern "C++"
41 #endif
示例1-基本數據類型
下表列出了使用typeid操作符的表達式的值
1 int a; 2 double b; 3 char * c; 4 long d;
運算 | 描述 |
---|---|
typeid(a) == typeid(int) | true |
typeid(a) == typeid(float) | false |
typeid(a) == typeid(int *) | false |
typeid(b) == typeid(double) | true |
typeid(b) == typeid(float) | false |
typeid(b) == typeid(long double) | false |
typeid(c) == typeid(char *) | true |
typeid(c) == typeid(char) | false |
typeid(c) == typeid(string) | false |
typeid(d) == typeid(long) | true |
typeid(d) == typeid(int) | false |
操作符typeid返回的是一個type_info類(用於描述數據類型的一個系統類)對象的引用。這個操作符可以用於表達式和類型名(包括自定的數據類型,比如類)。
示例2-類對象
1 class base
2 { 3 public : 4 void m(){cout<<"base"<<endl;} 5 }; 6 class derived : public base
7 { 8 public: 9 void m(){cout<<"derived"<<endl;} 10 };
假設我們根據例2中定義的兩個類來定義如下指針:
1 base * p = new derived;
下表將給出使用typeid操作符的結果。
運算 | 描述 |
---|---|
typeid(p) == typeid(base*) | true |
typeid(p) == typeid(derived*) | false |
typeid(*p) == typeid(base) | true |
typeid(*p) == typeid(derived) | false |
對於表達式typeid(p),同樣,因為p是base*類型的指針,因此typeid(p) == typeid(base*)為真,而typeid(p) == typeid(derived*)為假。而對於表達式typeid(*p),由於此時的基類不具有多態性,因而*p將會采用編譯期類型來計算,編譯期*p是base對象,因此表達式typeid(*p) == typeid(derived)為假,typeid(*p) == typeid(base)為真。
示例3-帶虛函數的基類
1 class base
2 { 3 public : 4 virtual void m(){cout<<"base"<<endl;} 5 }; 6 class derived : public base
7 { 8 public: 9 void m(){cout<<"derived"<<endl;} 10 };
假設我們如本例所示定義了兩個類base類和derived類,基於這兩個類定義,我們定義指針如下:
1 base * p = new derived;
下表將給出使用typeid操作符的結果。
運算 | 描述 |
---|---|
typeid(p) == typeid(base*) | true |
typeid(p) == typeid(derived*) | false |
typeid(*p) == typeid(base) | false |
typeid(*p) == typeid(derived) | true |
對於表達式typeid(p),因為p是base*類型的指針,因此typeid(p) == typeid(base*)為真,而typeid(p) == typeid(derived*)為假。而對於表達式typeid(*p),因為base類具有多態性,因而在計算typeid(*p)時會根據運行時p所指向的實際類型去計算,而本例中p指向的是派生類對象,因此表達式typeid(*p) == typeid(derived)為真,typeid(*p) == typeid(base)為假。
異常處理bad_typeid
1 class bad_typeid : public exception 2 { 3 public: 4 bad_typeid () throw() { } 5
6 // This declaration is not useless: 7 // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
8 virtual ~bad_typeid() throw(); 9
10 // See comment in eh_exception.cc.
11 virtual const char* what() const throw(); 12 }; 13 } // namespace std