C++是靜態類型語言,其數據類型是在編譯期就肯定的,不能在運行時更改。
C++語言中,靜態類型是對象自身的類型,動態類型是指針(引用)所指向對象的實際類型。
RTTI(Run-Time Type Information)即運行時類型識別,C++經過RTTI實現對多態的支持。
爲了支持RTTI,C++提供了一個type_info類和typeid與dynamic_cast兩個關鍵字。ios
type_info :
存儲特色類型的相關信息,經常使用來比較對象類型,type_info類的具體內容由編譯器實現來決定。其聲明以下:安全
class type_info { public: virtual ~type_info(); bool operator== (const type_info& rhs) const; bool operator!= (const type_info& rhs) const; bool before (const type_info& rhs) const; const char* name() const; private: type_info (const type_info& rhs); type_info& operator= (const type_info& rhs); };
type_info的構造函數和賦值操做符爲私有,所以,程序中建立type_info對象的惟一方法是使用typeid操做符。C++標準只是告訴編譯器須要實現type_info::name函數,但不一樣的編譯器實現各不相同,所以typeid(int).name()不一樣編譯器編譯運行後輸出不同。ide
typeid:
typeid語法規則以下:typeid(expr);
typeid表達式返回type_info類型,expr能夠是各類類型名,對象和內置基本數據類型的實例、指針或者引用。看成用於指針和引用時,將返回實際指向對象的類型信息。
若是表達式的類型是類類型且至少包含有一個虛函數,則typeid操做符返回表達式的動態類型,須要在運行時肯定;不然,typeid操做符返回表達式的靜態類型,在編譯時就能夠肯定。
當把typeid做用於指針的解引用p時,若指針p爲0,則:若是p指向的類型是帶虛函數的類類型,則typeid(p)在運行時拋出一個bad_typeid異常;不然,typeid(*p)的結果與p的值是不相關的,在編譯時就能夠肯定。函數
dynamic_cast:
動態類型轉換,運行時類型安全檢查。dynamic_cast會檢查待轉換的源對象是否真的能夠轉換成目標類型,這種檢查不是語法上的,而是真實狀況的。許多編譯器都是經過vtable找到對象的RTTI信息的,若是基類沒有虛方法,也就沒法判斷一個基類指針變量所指對象的真實類型。
dynamic_cast將一個指向基類的指針轉換爲一個指向派生類的指針,若是不能正確轉換,則返回空指針。
C++語言提供了typeid關鍵字用於獲取類型信息,typeid關鍵字返回對應參數的類型信息。typeid返回一個type_info類對象,當typeid的參數爲NULL時將拋出異常。typeid的參數既能夠時類型也能夠是變量,當參數爲類型,返回靜態類型信息;當參數爲變量,若是不存在虛函數表,返回靜態類型信息,若是存在虛函數表,返回動態類型信息。
typeid操做符的返回結果是名爲type_info的標準庫類型的對象的引用。
typeid在不一樣C++編譯器實現是不一樣的。學習
RTTI(Run-Time Type Identification,運行時類型識別)spa
C++類型轉換分爲向上類型轉換和向下類型轉換。指針
C++語言中,向上類型轉換描述的是子類向基類的強制類型轉換,是一種隱式類型轉換。在向上類型轉換過程當中,覆蓋方法和子類對象數據丟失的現象稱爲切割。code
#include <iostream> using namespace std; class Base { public: Base(int value = 0) { data = value; } virtual void print() { cout << "Base::print data = " << data << endl; } protected: int data; }; class Derived : public Base { public: Derived(int value = 0) { data = value; } virtual void print() { cout << "Derived print data = " << data << endl; } protected: int data; }; int main(int argc, char *argv[]) { Derived d(100); //將子類向上轉型爲基類 Base b = d;//直接賦值,產生切割 b.print();//Base::print data = 0 Base& rb = d;//引用賦值,不產生切割 rb.print();//Derived print data = 100 Base* pb = &d;//指針賦值,不產生切割 pb->print();//Derived print data = 100 //Derived* dp = pb;//error,不容許隱式向下轉型 return 0; }
在向上強制轉換過程當中,使用指針和引用不會形成切割,而使用直接賦值會形成切割。orm
C++語言中,向下類型轉換描述的是基類向子類的強制類型轉換,使用dynamic_cast進行向下強制類型轉換。dynamic_cast會在運行時進行類型檢查。若是向下轉型是安全的(若是基類指針或者引用實際指向一個派生類的對象),dynamic_cast會返回類型轉換後的指針。若是向下轉型不安全(即基類指針或者引用沒有指向一個派生類的對象),dynamic_cast返回空指針。 使用dynamic_cast時,類中必須定義虛函數。對象
#include <iostream> using namespace std; class Base { public: Base(int value = 0) { data = value; } virtual void print() { cout << "Base::print data = " << data << endl; } protected: int data; }; class Derived : public Base { public: Derived(int value = 0) { data = value; } virtual void print() { cout << "Derived print data = " << data << endl; } protected: int data; }; int main(int argc, char *argv[]) { //指針 Base* bp1 = new Base(101); Derived* dp11 = static_cast<Derived*>(bp1); cout << "Base" << endl; cout << bp1 << endl; cout << dp11<< endl; dp11->print();//Base::print data = 101 Derived* dp12 = dynamic_cast<Derived*>(bp1); cout << dp12 << endl;//0,向下轉型失敗 if(dp12 != NULL) { dp12->print(); } Base* bp2 = new Derived(102); Derived* dp21 = static_cast<Derived*>(bp2); cout << "Derived" << endl; cout << bp2 << endl; cout << dp21<< endl; dp21->print();//Derived print data = 102 Derived* dp22 = dynamic_cast<Derived*>(bp2); cout << dp22 << endl;//向下轉型成功 if(dp22 != NULL) { dp22->print();//Derived print data = 102 } //引用 Base b1(10); Derived& rd11 = static_cast<Derived&>(b1); rd11.print();//Base::print data = 10 //Derived& rd12 = dynamic_cast<Derived&>(b1);//exception Derived b2(10); Derived& rd21 = static_cast<Derived&>(b2); rd21.print();//Derived print data = 10 Derived& rd22 = dynamic_cast<Derived&>(b2); rd22.print();//Derived print data = 10 return 0; }
上述代碼中,若是指針、引用實際指向的對象爲派生類對象,使用static_cast、dynamic_cast轉換都是安全的;若是指針、引用實際指向的對象爲基類對象,使用dynamic_cast會返回NULL指針或拋出異常,使用static_cast關鍵字返回執行基類對象的指針或引用,不能訪問派生類的覆蓋方法與成員。
#include <iostream> using namespace std; class BaseA { public: BaseA(int value = 0) { data = value; } virtual void printA() { cout << "BaseA::print data = " << data << endl; } protected: int data; }; class BaseB { public: BaseB(int value = 0) { data = value; } virtual void printB() { cout << "BaseB::print data = " << data << endl; } protected: int data; }; class Derived : public BaseA, public BaseB { public: Derived(int value = 0) { data = value; } virtual void printA() { cout << "Derived printA data = " << data << endl; } virtual void printB() { cout << "Derived printB data = " << data << endl; } protected: int data; }; int main(int argc, char *argv[]) { //BaseA cout << "BaseA" << endl; BaseA* bpa = new BaseA(10); cout << bpa << endl; Derived* pd1 = static_cast<Derived*>(bpa); cout << pd1 << endl; pd1->printA();//BaseB::print data 10 //pd1->printB();//exception,實際指向BaseA對象,沒有printB方法 Derived* pd2 = dynamic_cast<Derived*>(bpa); cout << pd2 << endl;//0,向下轉型失敗 if(pd2 != NULL) { pd2->printA(); pd2->printB(); } //BaseB cout << "BaseB" << endl; BaseB* bpb = new BaseB(10); cout << bpb << endl; //pd3指向bpb前8字節的地址 Derived* pd3 = static_cast<Derived*>(bpb); cout << pd3 << endl; //pd3->printA();//exception //pd3->printB();//exception Derived* pd4 = dynamic_cast<Derived*>(bpb); cout << pd4 << endl;//0,向下轉型失敗 if(pd4 != NULL) { pd4->printA(); pd4->printB(); } cout << "Derived" << endl; BaseA* bpd = new Derived(101); cout << bpd << endl; Derived* pd5 = static_cast<Derived*>(bpd); cout << pd5 << endl; pd5->printA();//Derived printA data = 101 pd5->printB();//Derived printB data = 101 Derived* pd6 = dynamic_cast<Derived*>(bpd); cout << pd6 << endl; if(pd6 != NULL) { pd6->printA();//Derived printA data = 101 pd6->printB();//Derived printB data = 101 } BaseA* pa = static_cast<BaseA*>(bpd); pa->printA(); //BaseB* pb = static_cast<BaseB*>(bpd);//error, BaseB* pb = dynamic_cast<BaseB*>(bpd);//正確, pb->printB(); cout << "Derived+" << endl; Derived* dpd = new Derived(102); cout << dpd << endl; BaseA* dpa = static_cast<BaseA*>(dpd); cout << dpa << endl; dpa->printA(); BaseB* dpb1 = static_cast<BaseB*>(dpd);// cout << dpb1 << endl; dpb1->printB(); BaseB* dpb2 = dynamic_cast<BaseB*>(dpd);// cout << dpb2 << endl; dpb2->printB(); return 0; }
上述代碼中,bpa指針指向BaseA對象,使用static_cast關鍵字對bpa進行向下轉型爲Derived指針對象時,返回bpa的值,因爲實際指向BaseA對象,所以對BaseB方法時會致使異常;使用dynamic_cast關鍵字對bpa進行向下轉型時,轉型失敗,返回NULL。
bpb指針實際指向BaseB對象,使用static_cast關鍵字對bpb進行向下轉型爲Derived指針對象時,返回bpb地址的-8字節的地址,該地值是一個不合法的Derived對象地址,所以對該地址調用BaseA、BaseB類的方法時會致使異常;使用dynamic_cast關鍵字對bpa進行向下轉型時,轉型失敗,返回NULL。
bpd指針實際指向Derived對象,使用static_cast關鍵字對bpd進行向下轉型爲Derived指針對象時,返回bpd的值,能夠合法調用BaseA、BaseB類的方法;使用dynamic_cast關鍵字對bpd進行向下轉型時,返回bpd的值,能夠合法調用BaseA、BaseB類的方法。若是使用static_cast關鍵字將BaseA類型指針bpd轉型爲BaseB指針時,C++編譯器報錯;必須使用dynamic_cast關鍵字,dynamic_cast會在運行時對指針進行調整。
Derived類型的dpd指針指向Derived對象,使用static_cast關鍵字和dynamic_cast關鍵字均可以進行向上轉型。
所謂內省是指面嚮對象語言的一種在運行期間查詢對象信息的能力, 好比若是語言具備運行期間檢查對象型別的能力,那麼語言是型別內省(type intropection)的,型別內省能夠用來實施多態。C++的內省比較有限,僅支持型別內省, C++的型別內省是經過運行時類型識別(RTTI)(Run-Time Type Information)中的typeid 以及dynamic_case關鍵字來實現的。