C++中運行時類型識別(RTTI)
1、dynamic_cast操作符
將基類類型的指標或引用安全的轉換為衍生類別類型的指標或引用。
注意:基類至少帶有一個虛函數,這樣源類型才可能是多態的,才會在運行時確定類型。否則將導致編譯錯誤。
如果轉換成功,則返回一個指向轉換後類型的指標或引用;如果轉換失敗,指標類型的轉換結果為0,參考型別則拋出bad_cast類型的異常。
如果是指標類型,運算元可以是0。但是沒有空類型的引用。
測試代碼:
#include <iostream>#include <typeinfo>using namespace std;class B{public: virtual void f() { cout<<"B::f"<<endl; } void ff() { cout<<"B::ff"<<endl; }};class D:public B{public: void f() { cout<<"D::f"<<endl; } void g() { cout<<"D::g"<<endl; }};void ta(){///pointer B *b=0; D *d=0; b = new D(); b->f();///D::f d = dynamic_cast<D*>(b); if(d) { d->g(); }///D::g else { b->ff(); } b = new B(); d = dynamic_cast<D*>(b); if(d) { d->g(); } else { b->ff(); }///B::ff}void tb(){ D d; B c; B &b=d; try { D & dt = dynamic_cast<D&>(b); dt.g(); } catch(bad_cast) { cout<<"dynamic cast failed"<<endl; } B &br = c;///如果這裡寫b=c;將發生奇怪的現象。 try { D & dt = dynamic_cast<D&>(br); dt.g(); } catch(bad_cast) { cout<<"dynamic cast failed"<<endl; }}int main(){ ta(); cout<<"-------"<<endl; tb(); return 0;}
運行結果:
D::fD::gB::ff-------D::gdynamic cast failed
2、typeid操作符
typeid的運算式:
typeid(e)
其中e可以是任意運算式或者類型名。
如果運算式的類型是類類型且該類包含至少1個虛函數,則運算式的動態類型(實際類型)可能不同於它的靜態類型。
如果運算元的不是類類型,或者是沒有虛函數的類,則typeid操作符指出運算元的靜態類型。如果運算元定義了至少一個虛函數,則在運行時計算類型。
ISO C++標準並沒有確切定義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>#include <typeinfo>using namespace std;class B{public: virtual void f() { cout<<"B::f"<<endl; } void ff() { cout<<"B::ff"<<endl; }};class D:public B{public: void f() { cout<<"D::f"<<endl; } void g() { cout<<"D::g"<<endl; }};void tc(){ D d; B b; int k=0; cout<<typeid(k).name()<<","<<typeid(int).name()<<endl; cout<<typeid(float).name()<<endl; cout<<typeid(b).name()<<endl; cout<<typeid(d).name()<<endl; B *bp=new D(); cout<<"prt:"<<typeid(bp).name()<<endl;//直接判斷指標,而不是指標所指向的對象的類型 cout<<typeid(*bp).name()<<endl; bp = new B(); cout<<typeid(*bp).name()<<endl; cout<<typeid(*bp).before(typeid(char))<<endl; cout<<typeid(char).before(typeid(*bp))<<endl; cout<<typeid(B).before(typeid(D))<<endl;}int main(){ tc(); return 0;}
結果:
i,if1B1Dprt:P1B1D1B101
這裡使用的是G++,vs可能與此不同。