C++中的類型判斷typeid()操作與java中的 instanceof 做比較


這是RTTI(運行階段類型識別)的問題,c++有三個支持RTTI的元素:
1. dynamic_cast 操作符
    如果可能的話,dynamic_cast操作符將使用一個指向基類的指針來生成一個指向派生類的指針;否則,該操作符返回空指針。這是最常用的 RTTI組件,它不能回答“指針指向的是哪類對象”這樣的問題,但他能回答“是否可以安全地將對象的地址賦給特定類型的指針”這樣的問題。如:
class A{}
class B: public A{}
class C: public B{}
然后有下面的指針:
A *a = new A;
B *b = new B;
C *c = new C;
則:
C *cc1 = dynamic_cast<C*>(c);    //安全
C *cc2 = dynamic_cast<C*>(a);    //cc2是空指針
C *cc3 = dynamic_cast<c*>(b);    //cc3是空指針
B *bb = dynamic_cast<C*>(b);     //安全
注:只能將此RTTI用於包含虛函數的類層次結構,原因在於只有對於這種類層次結構,才應該將派生類對象的地址賦給基類指針。

2. typeid操作符
3. type_info結構,(須包含頭文件<typeinfo>)
class A{}
class B: public A{}
class C: public B{}
然后有下面的指針:
A *pa = new A;
B *pb = new B;
C *pc = new C;
則:
typeid(C) == typeid(pc);  //值為true
typeid(B) == typeid(pc);  //值為false

type_info有一個name()成員函數,返回一個類名的字符串:
cout << typeid(*pc).name();  //打印C類的類名

注:typeid操作符和name()成員函數都適用於dynamic_cast和virtual函數不能處理的情況

以下總結:

  轉自博客:http://www.cppblog.com/smagle/archive/2010/05/14/115286.html

  開typeid神秘面紗之前,我們先來了解一下RTTI(Run-Time Type Identification,運行時類型識別),它使程序能夠獲取由基指針或引用所指向的對象的實際派生類型,即允許“用指向基類的指針或引用來操作對 象”的程序能夠獲取到“這些指針或引用所指對象”的實際派生類型。在C++中,為了支持RTTI提供了兩個操作符:dynamic_cast和 typeid。
    dynamic_cast允許運行時刻進行類型轉換,從而使程序能夠在一個類層次結構中安全地轉化類型,與之相對應的還有一個非安全的轉換操作符 static_cast,因為這不是本文的討論重點,所以這里不再詳述,感興趣的可以自行查閱資料。下面就開始今天我們的話題:typeid。
    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的字符串,用來表示相應的類型名,但務必注意這個返回的類型名與程序中使用的相應類型名 並不一定一致(往往如此,見后面的程序),這具體由編譯器的實現所決定的,標准只要求實現為每個類型返回唯一的字符串。

  測試程序如下:

 

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Base {};
 5 class Derived: public Base {};
 6 
 7 int main()
 8 {
 9     Base b, *pb;
10     pb = NULL;
11     Derived d;
12 
13     cout << typeid(int).name() << endl
14          << typeid(unsigned).name() << endl
15          << typeid(long).name() << endl
16          << typeid(unsigned long).name() << endl
17          << typeid(char).name() << endl
18          << typeid(unsigned char).name() << endl
19          << typeid(float).name() << endl
20          << typeid(double).name() << endl
21          << typeid(string).name() << endl
22          << typeid(Base).name() << endl
23          << typeid(b).name()<<endl
24          << typeid(pb).name()<<endl
25          << typeid(Derived).name() << endl
26          << typeid(d).name()<<endl
27          << typeid(type_info).name() << endl;
28          
29     return 0;
30 }

 

不同的編譯器輸出如下:用MS的V8和GUN的GCC編譯該段代碼並運行,結果分別為下面的左右二圖

 

      

  對比代碼以及上面的文字描述,不知道各位是否已經有所明了(這里需要注意的是Base類的對象b和對象指針pb,他們的輸出)。
    考慮到V8的輸出很直觀,所以我采用V8來做實驗。下面對上面的代碼稍微添加一點內容,如下:

 

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含有虛函數,注意看結果,指針仍然是Base*的,盡管他們指向的是底層對象Derived,而這些Base對象的類型卻是Derived的。
    因為指針pb3不是類類型,所以typeid就返回該指針pb3的指針類型Base *。而*pb3是一個類類型的表達式,而且該類帶有虛函數,所以指出該pb3指向的底層對象的類型Derived。
    如果typeid操作符的操作數是至少包含一個虛擬函數的類類型時,並且該表達式是一個基類的引用,則typeid操作符指出底層對象的派生類類型。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM