C++ typeid關鍵字詳解

xxx發表於2020-11-10

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_casttypeid

  • 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

相關文章