類型識別是面向對象中引入的一個新概念,主要用來判斷賦值兼容性原則中的類型問題,即此時的數據類型究竟是基類類型仍是派生類類型?ios
當基類指針指向子類對象 或者 基類引用成爲子類對象的別名 時,就須要使用類型識別;安全
1 Base *p = new Derived(); 2 Base &r = *p
對於上面的語句,咱們能夠這樣認識,指針p是Base類型,可是P 又指向了一個新的Derived類型,此時很難判斷指針P 的數據類型;同理,引用r 原本做爲父類的別名而存在,但因爲賦值兼容性,引用r也能夠做爲子類的別名,一樣此時 引用 r 的數據類型也不能肯定;ide
注:1)由以前所學知識,若沒有虛函數重寫,編譯器爲了安全起見,會將指針p 看成 Base 類型;(編譯期間) 函數
2)如有虛函數重寫,就會發生動態多態特性,此時就會根據指針p 所指向的具體數據類型來肯定指針p 的數據類型。(運行期間)spa
1)靜態類型:變量(對象)自身的類型;在編譯階段就能肯定所使用變量的數據類型。指針
2)動態類型:指針(引用)所指向對象的實際類型;在運行階段根據指針所指向的具體數據類型來肯定所使用的數據類型。code
Base *b 所指向的實際對象沒法肯定,若指針b 指向的是子類對象,則程序正常運行;若指針b 指向的是父類對象,則程序有可能出現 Bug;對象
注:在 g++ 編譯器下上述狀況都可正常運行,但後者不建議使用;blog
在賦值兼容原則中,基類指針是否能夠強制類型轉換爲子類指針取決於動態類型;(很重要!!!)--- 只有動態類型是子類對象才能進行合法轉換原型
1)必須從基類開始提供類型虛函數;
2)全部的派生類都必須重寫類型虛函數;
3)每一個派生類的類型 ID必須惟一;
結果:調用類型虛函數就能夠知道當前的對象到底是什麼類型,這樣就能夠獲得動態類型,達到動態類型識別效果;
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Base 7 { 8 public: 9 enum { ID = 0 }; 10 11 virtual int type() // 類型虛函數 12 { 13 return ID; 14 } 15 }; 16 17 class Derived : public Base 18 { 19 public: 20 enum { ID = 1 }; 21 22 int type() 23 { 24 return ID; 25 } 26 27 void print() 28 { 29 cout << "I'm a Derived. " << endl; 30 } 31 }; 32 33 class Child : public Base 34 { 35 public: 36 enum { ID = 2 }; 37 38 int type() 39 { 40 return ID; 41 } 42 }; 43 44 void test(Base* pb) 45 { 46 if( pb->type() == Child::ID ) 47 { 48 Child* pc = static_cast<Child*>(pb); 49 //Child* pc = dynamic_cast<Child*>(pb); // 同上 50 51 cout << "& = " << pc << endl; 52 cout << "I'm a Child. " << endl; 53 } 54 55 if( pb->type() == Derived::ID ) 56 { 57 Derived* pd = static_cast<Derived*>(pb); 58 //Derived* pd = dynamic_cast<Derived*>(pb); // 同上 59 60 cout << "& = " << pd << endl; 61 pd->print(); 62 } 63 64 if( pb->type() == Base::ID ) 65 { 66 cout << "& = " << pb << endl; 67 cout << "I'm a Base. " << endl; 68 } 69 } 70 71 int main(int argc, char *argv[]) 72 { 73 Base b; 74 Derived d; 75 Child c; 76 77 test(&b); 78 test(&d); 79 test(&c); 80 81 return 0; 82 } 83 /** 84 * 運行結果: 85 * & = 0x7ffccf0dd850 86 * I'm a Base. 87 * & = 0x7ffccf0dd860 88 * I'm a Derived. 89 * & = 0x7ffccf0dd870 90 * I'm a Child. 91 */
1)dynamic_cast這個關鍵字若是要轉換的實際類型和指定的類型不同,則會返回NULL。例如當指定類型爲子類對象時,若是父類指針的動態類型是這個子類對象時,轉換成功,而動態類型是父類對象或者其餘子類對象時,轉換失敗;
2)dynamic_cast 要求使用的目標對象類型必須是多態,即:所在類族至少有一個虛函數;
3)只能用於指針和引用之間的轉換
1. 用於指針轉換時,轉換失敗,返回空指針;
2. 用於引用轉換時,轉換失敗,將引起 bad_cast異常。
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Base 7 { 8 public: 9 virtual ~Base() 10 { 11 12 } 13 }; 14 15 class Derived : public Base 16 { 17 public: 18 void print() 19 { 20 cout << "I'm a Derived. " << endl; 21 } 22 }; 23 24 class Child : public Base 25 { 26 27 }; 28 29 void test(Base* pb) 30 { 31 // dynamic_cast 只能肯定最終的轉化結果,沒法獲取動態類型的原型 32 Derived* pd = dynamic_cast<Derived*>(pb); 33 34 if(pd != NULL) 35 { 36 // Derived 類類型, 可使用指針pd訪問Derived類的成員 37 cout << "& = " << pd << endl; 38 pd->print(); 39 } 40 else 41 { 42 Child* pc = dynamic_cast<Child*>(pb); 43 44 if(pc != NULL) 45 { 46 // Child 類類型, 可使用指針pc訪問Child類的成員 47 cout << "& = " << pc << endl; 48 cout << "I'm a Child. " << endl; 49 } 50 else 51 { 52 // Base 類類型, 可使用指針pb訪問Base類的成員 53 cout << "& = " << pc << endl; 54 cout << "I'm a Base. " << endl; 55 } 56 } 57 } 58 59 int main(int argc, char *argv[]) 60 { 61 Base b; 62 Derived d; 63 Child c; 64 65 test(&b); 66 test(&d); 67 test(&c); 68 69 return 0; 70 } 71 /** 72 * 運行結果: 73 * & = 0 74 * I'm a Base. 75 * & = 0x7ffccf0dd860 76 * I'm a Derived. 77 * & = 0x7ffccf0dd870 78 * I'm a Child. 79 */
1)typeid 是一個關鍵字,專門用於動態類型識別;
2)typeid 關鍵字返回對應參數的類型信息,此類型信息是一個type_info類對象;
1. 當參數爲類型時,返回靜態類型信息;
2. 當參數爲變量時:1> 參數變量內部不存在虛函數表時,返回靜態類型信息; 2> 參數變量內部存在虛函數表時,返回動態類型信息;
3. 當參數爲 NULL 時,將拋出異常;
3)typeid 使用時須要包含頭文件<typeinfo>;
4)typeid 使用時直接指定對象或者類型。
5)typeid 在不一樣的編譯器內部實現是不一樣的;
1 int i = 0; 2 3 const type_info& tiv = typeid(i); // 將 i 的類型信息放到 type_info 中去; 4 const type_info& tii = typeid(int); 5 6 cout << (tiv == tii) << endl; // 1
1 #include <iostream> 2 #include <string> 3 #include <typeinfo> 4 5 using namespace std; 6 7 class Base 8 { 9 public: 10 virtual ~Base() 11 { 12 } 13 }; 14 15 class Derived : public Base 16 { 17 public: 18 void print() 19 { 20 cout << "I'm a Derived." << endl; 21 } 22 }; 23 24 class Child : public Base 25 { 26 public: 27 void print() 28 { 29 cout << "I'm a Child." << endl; 30 } 31 }; 32 33 void test(Base* pb) 34 { 35 const type_info& tb = typeid(*pb); 36 37 if( tb == typeid(Derived) ) 38 { 39 Derived* pd = dynamic_cast<Derived*>(pb); 40 41 cout << "& = " << pd << endl; 42 pd->print(); 43 } 44 else if( tb == typeid(Child) ) 45 { 46 Child* pc = dynamic_cast<Child*>(pb); 47 48 cout << "& = " << pc << endl; 49 pc->print(); 50 51 } 52 else if( tb == typeid(Base) ) 53 { 54 cout << "& = " << pb << endl; 55 cout << "I'm a Base. " << endl; 56 } 57 58 cout << tb.name() << endl; 59 } 60 61 int main(int argc, char *argv[]) 62 { 63 Base b; 64 Derived d; 65 Child c; 66 int index; 67 char ch; 68 69 const type_info& tp = typeid(b); 70 const type_info& tc = typeid(d); 71 const type_info& tn = typeid(c); 72 const type_info& ti = typeid(index); 73 const type_info& tch = typeid(ch); 74 75 cout<<tp.name()<<endl; 76 cout<<tc.name()<<endl; 77 cout<<tn.name()<<endl; 78 cout<<ti.name()<<endl; 79 cout<<tch.name()<<endl; 80 81 test(&b); 82 test(&d); 83 test(&c); 84 85 return 0; 86 } 87 /** 88 * 運行結果: 89 * 4Base 90 * 7Derived 91 * 5Child 92 * i 93 * c 94 * & = 0x7ffcbd4d6280 95 * I'm a Base. 96 * 4Base 97 * & = 0x7ffcbd4d6290 98 * I'm a Derived. 99 * 7Derived 100 * & = 0x7ffcbd4d62a0 101 * I'm a Child. 102 * 5Child 103 */
3 種動態類型的實現方法 建議選 第3種 (typeid)。
對於多態實現,存在如下缺陷:
1)必須從基類開始提供類型虛函數;
2)全部的派生類都必須重寫類型虛函數;
3)每一個派生類的類型名必須惟一;
對於 dynamic_cast 實現,只能獲得類型轉換的結果,不能獲取真正的動態類型,同時 dynamic_cast 必須多態實現。