轉:https://blog.csdn.net/TuxedoLinux/article/details/80604377
c++有種機制叫做RTTI(Run-Time Type Identification,運行時類型識別),它使程序能夠獲取由基指針或引用所指向的對象的實際派生類型,即允許“用指向基類的指針或引用來操作對象”的程序能夠獲取到“這些指針或引用所指對象”的實際派生類型。在C++中,為了支持RTTI提供了兩個操作符:dynamic_cast和typeid。 dynamic_cast允許運行時刻進行類型轉換,從而使程序能夠在一個類層次結構中安全地轉化類型,與之相對應的還有一個非安全的轉換操作符static_cast,typeid是C++的關鍵字之一,等同於sizeof這類的操作符。typeid操作符的返回結果是名為type_info的標准庫類型的對象的引用(在頭文件typeinfo中定義,稍后我們看一下vs和gcc庫里面的源碼),它的表達式有下圖兩種形式
如果表達式的類型是類類型且至少包含有一個虛函數,則typeid操作符返回表達式的動態類型,需要在運行時計算;否則,typeid操作符返回表達式的靜態類型,在編譯時就可以計算。
ISO C++標准並沒有確切定義type_info,它的確切定義編譯器相關的,但是標准卻規定了其實現必需提供如下四種操作(在之后的章節中我會來分析type_info類文件的源碼):
t1 == t2 | 如果兩個對象t1和t2類型相同,則返回true;否則返回false |
t1 != t2 | 如果兩個對象t1和t2類型不同,則返回true;否則返回false |
t.name() | 返回類型的C-style字符串,類型名字用系統相關的方法產生 |
t1.before(t2) | 返回指出t1是否出現在t2之前的bool值 |
type_info類提供了public虛 析構函數,以使用戶能夠用其作為基類。它的默認構造函數和拷貝構造函數及賦值操作符都定義為private,所以不能定義或復制type_info類型的對象。程序中創建type_info對象的唯一方法是使用typeid操作符(由此可見,如果把typeid看作函數的話,其應該是type_info的 友元)。type_info的name成員函數返回C-style的字符串,用來表示相應的類型名,但務必注意這個返回的類型名與程序中使用的相應類型名並不一定一致(往往如此,見后面的程序),這具體由編譯器的實現所決定的,標准只要求實現為每個類型返回唯一的字符串。
下面將通過代碼和圖例來展示
#include <iostream> using namespace std; class Base {}; class Derived: public Base {}; int main() { Base b, *pb; pb = NULL; Derived d; cout << typeid(int).name() << endl << typeid(unsigned).name() << endl << typeid(long).name() << endl << typeid(unsigned long).name() << endl << typeid(char).name() << endl << typeid(unsigned char).name() << endl << typeid(float).name() << endl << typeid(double).name() << endl << typeid(string).name() << endl << typeid(Base).name() << endl << typeid(b).name()<<endl << typeid(pb).name()<<endl << typeid(Derived).name() << endl << typeid(d).name()<<endl << typeid(type_info).name() << endl; return 0; }
我分別用MS的V8和GUN的GCC編譯該段代碼並運行,結果分別為下面的左右二圖。
下面對上面的代碼稍微添加一點內容,如下:
Base *pb2 = dynamic_cast<Base *>(new Derived); Base &b2 = d; Base *pb3 = &d; cout << typeid(pb2).name() <<endl//輸出Base * << typeid(b2).name()<<endl //輸出Base << typeid(pb3).name()<<endl//輸出Base * << typeid(*pb3).name()<<endl;//輸出Base
因為Base不包含虛函數,所以typeid的結果指出,表達式的類型是Base或Base *型,盡管他們的底層對象是Derived。即:當typeid操作符的操作數是不帶有虛函數的類類型時,typeid操作符會指出操作數的類型,而不是底層對象的類型。
下面在對Base函數做一個小小調整,為其加上一個虛函數,再看輸出結果。
class Base {virtual void f(){}; }; /*...*/ cout << typeid(pb2).name() <<endl//輸出Base * << typeid(b2).name()<<endl //輸出Derived << typeid(pb3).name()<<endl//輸出Base * << typeid(*pb3).name()<<endl;//輸出Derived
這次Base含有虛函數,注意看結果,指針仍然是Base*的,盡管他們指向的是底層對象Derived,而這些Base對象的類型卻是Derived的。
因為指針pb3不是類類型,所以typeid就返回該指針pb3的指針類型Base *。而*pb3是一個類類型的表達式,而且該類帶有虛函數,所以指出該pb3指向的底層對象的類型Derived。
如果typeid操作符的操作數是至少包含一個虛擬函數的類類型時,並且該表達式是一個基類的引用,則typeid操作符指出底層對象的派生類類型。