程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++基礎知識 >> C++ typeid運算符

C++ typeid運算符

編輯:C++基礎知識
typeid 運算符用來獲取一個表達式的類型信息。類型信息對於編程語言非常重要,它描述了數據的各種屬性:
  • 對於基本類型(int、float 等C++內置類型)的數據,類型信息所包含的內容比較簡單,主要是指數據的類型。
  • 對於類類型的數據(也就是對象),類型信息是指對象所屬的類、所包含的成員、所在的繼承關系等。

類型信息是創建數據的模板,數據占用多大內存、能進行什麼樣的操作、該如何操作等,這些都由它的類型信息決定。

typeid 的操作對象既可以是表達式,也可以是數據類型,下面是它的兩種使用方法:

typeid( dataType )
typeid( expression )

dataType 是數據類型,expression 是表達式,這和 sizeof 運算符非常類似,只不過 sizeof 有時候可以省略括號( ),而 typeid 必須帶上括號。

typeid 會把獲取到的類型信息保存到一個 type_info 類型的對象裡面,並返回該對象的常引用;當需要具體的類型信息時,可以通過成員函數來提取。typeid 的使用非常靈活,請看下面的例子(只能在 VC/VS 下運行):
#include <iostream>
#include <typeinfo>
using namespace std;

class Base{ };

struct STU{ };

int main(){
    //獲取一個普通變量的類型信息
    int n = 100;
    const type_info &nInfo = typeid(n);
    cout<<nInfo.name()<<" | "<<nInfo.raw_name()<<" | "<<nInfo.hash_code()<<endl;

    //獲取一個字面量的類型信息
    const type_info &dInfo = typeid(25.65);
    cout<<dInfo.name()<<" | "<<dInfo.raw_name()<<" | "<<dInfo.hash_code()<<endl;

    //獲取一個對象的類型信息
    Base obj;
    const type_info &objInfo = typeid(obj);
    cout<<objInfo.name()<<" | "<<objInfo.raw_name()<<" | "<<objInfo.hash_code()<<endl;

    //獲取一個類的類型信息
    const type_info &baseInfo = typeid(Base);
    cout<<baseInfo.name()<<" | "<<baseInfo.raw_name()<<" | "<<baseInfo.hash_code()<<endl;

    //獲取一個結構體的類型信息
    const type_info &stuInfo = typeid(struct STU);
    cout<<stuInfo.name()<<" | "<<stuInfo.raw_name()<<" | "<<stuInfo.hash_code()<<endl;

    //獲取一個普通類型的類型信息
    const type_info &charInfo = typeid(char);
    cout<<charInfo.name()<<" | "<<charInfo.raw_name()<<" | "<<charInfo.hash_code()<<endl;

    //獲取一個表達式的類型信息
    const type_info &expInfo = typeid(20 * 45 / 4.5);
    cout<<expInfo.name()<<" | "<<expInfo.raw_name()<<" | "<<expInfo.hash_code()<<endl;

    return 0;
}
運行結果:
int | .H | 529034928
double | .N | 667332678
class Base | .?AVBase@@ | 1035034353
class Base | .?AVBase@@ | 1035034353
struct STU | .?AUSTU@@ | 734635517
char | .D | 4140304029
double | .N | 667332678

從本例可以看出,typeid 的使用非常靈活,它的操作數可以是普通變量、對象、內置類型(int、float等)、自定義類型(結構體和類),還可以是一個表達式。

本例中還用到了 type_info 類的幾個成員函數,下面是對它們的介紹:
  • name() 用來返回類型的名稱。
  • raw_name() 用來返回名字編碼(Name Mangling)算法產生的新名稱。關於名字編碼的概念,我們已在《C++函數編譯原理和成員函數的實現》中講到。
  • hash_code() 用來返回當前類型對應的 hash 值。hash 值是一個可以用來標志當前類型的整數,有點類似學生的學號、公民的身份證號、銀行卡號等。不過 hash 值有賴於編譯器的實現,在不同的編譯器下可能會有不同的整數,但它們都能唯一地標識某個類型。

遺憾的是,C++ 標准只對 type_info 類做了很有限的規定,不僅成員函數少,功能弱,而且各個平台的實現不一致。例如上面代碼中的 name() 函數,nInfo.name()objInfo.name()在 VC/VS 下的輸出結果分別是intclass Base,而在 GCC 下的輸出結果分別是i4Base

C++ 標准規定,type_info 類至少要有如下所示的 4 個 public 屬性的成員函數,其他的擴展函數編譯器開發者可以自由發揮,不做限制。

1) 原型:const char* name() const;

返回一個能表示類型名稱的字符串。但是C++標准並沒有規定這個字符串是什麼形式的,例如對於上面的objInfo.name()語句,VC/VS 下返回“class Base”,但 GCC 下返回“4Base”。

2) 原型:bool before (const type_info& rhs) const;

判斷一個類型是否位於另一個類型的前面,rhs 參數是一個 type_info 對象的引用。但是C++標准並沒有規定類型的排列順序,不同的編譯器有不同的排列規則,程序員也可以自定義。要特別注意的是,這個排列順序和繼承順序沒有關系,基類並不一定位於派生類的前面。

3) 原型:bool operator== (const type_info& rhs) const;

重載運算符“==”,判斷兩個類型是否相同,rhs 參數是一個 type_info 對象的引用。

4) 原型:bool operator!= (const type_info& rhs) const;

重載運算符“!=”,判斷兩個類型是否不同,rhs 參數是一個 type_info 對象的引用。
關於運算符重載,我們將在《C++運算符重載》一章中詳細講解。
raw_name() 是 VC/VS 獨有的一個成員函數,hash_code() 在 VC/VS 和較新的 GCC 下有效。

可以發現,不像 Java、C# 等動態性較強的語言,C++ 能獲取到的類型信息非常有限,也沒有統一的標准,如同“雞肋”一般,大部分情況下我們只是使用重載過的“==”運算符來判斷兩個類型是否相同。

判斷類型是否相等

typeid 運算符經常被用來判斷兩個類型是否相等。

1) 內置類型的比較

例如有下面的定義:
char *str;
int a = 2;
int b = 10;
float f;
類型判斷結果為:
類型比較 結果 類型比較 結果 typeid(int) == typeid(int) true typeid(int) == typeid(char) false typeid(char*) == typeid(char) false typeid(str) == typeid(char*) true typeid(a) == typeid(int) true typeid(b) == typeid(int) true typeid(a) == typeid(a) true typeid(a) == typeid(b) true typeid(a) == typeid(f) false typeid(a/b) == typeid(int) true
typeid 返回 type_info 對象的引用,而表達式typeid(a) == typeid(b)的結果為 true,可以說明,一個類型不管使用了多少次,編譯器都只為它創建一個對象,所有 typeid 都返回這個對象的引用。

需要提醒的是,為了減小編譯後文件的體積,編譯器不會為所有的類型創建 type_info 對象,只會為使用了 typeid 運算符的類型創建。不過有一種特殊情況,就是帶虛函數的類(包括繼承來的),不管有沒有使用 typeid 運算符,編譯器都會為帶虛函數的類創建 type_info 對象,我們將在《C++ RTTI機制(運行時類型識別)》中展開講解。

2) 類的比較

例如有下面的定義:
class Base{};
class Derived: public Base{};

Base obj1;
Base *p1;
Derived obj2;
Derived *p2 = new Derived;
p1 = p2;
類型判斷結果為:
類型比較 結果 類型比較 結果 typeid(obj1) == typeid(p1) false typeid(obj1) == typeid(*p1) true typeid(&obj1) == typeid(p1) true typeid(obj1) == typeid(obj2) false typeid(obj1) == typeid(Base) true typeid(*p1) == typeid(Base) true typeid(p1) == typeid(Base*) true typeid(p1) == typeid(Derived*) false
表達式typeid(*p1) == typeid(Base)typeid(p1) == typeid(Base*)的結果為 true 可以說明:即使將派生類指針 p2 賦值給基類指針 p1,p1 的類型仍然為 Base*。

type_info 類的聲明

最後我們再來看一下 type_info 類的聲明,以進一步了解它所包含的成員函數以及這些函數的訪問權限。type_info 類位於typeinfo頭文件,聲明形式類似於:
class type_info {
public:
    virtual ~type_info();
    int operator==(const type_info& rhs) const;
    int operator!=(const type_info& rhs) const;
    int before(const type_info& rhs) const;
    const char* name() const;
    const char* raw_name() const;
private:
    void *_m_data;
    char _m_d_name[1];
    type_info(const type_info& rhs);
    type_info& operator=(const type_info& rhs);
};
它的構造函數是 private 屬性的,所以不能在代碼中直接實例化,只能由編譯器在內部實例化(借助友元)。而且還重載了“=”運算符,也是 private 屬性的,所以也不能賦值。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved