1、類型識別的相關(guān)概念
(1)類型識別的作用
類型識別是面向?qū)ο笾幸氲囊粋€(gè)新概念,主要用來判斷賦值兼容性原則中的類型問題,即此時(shí)的數(shù)據(jù)類型到底是基類類型還是派生類類型?
當(dāng)基類指針指向子類對象? 或者?基類引用成為子類對象的別名? 時(shí),就需要使用類型識別;
1 Base *p = new Derived(); 2 Base &r = *p
? ? ? ?對于上面的語句,我們可以這樣認(rèn)識,指針p是Base類型,但是P 又指向了一個(gè)新的Derived類型,此時(shí)很難判斷指針P
的數(shù)據(jù)類型;同理,引用r 本來作為父類的別名而存在,但由于賦值兼容性,引用r也可以作為子類的別名,同樣此時(shí) 引用 r 的數(shù)據(jù)類型也不能確定;
注:1)由之前所學(xué)知識,若沒有虛函數(shù)重寫,編譯器為了安全起見,會(huì)將指針p 當(dāng)作 Base 類型;(編譯期間)
2)若有虛函數(shù)重寫,就會(huì)發(fā)生動(dòng)態(tài)多態(tài)特性,此時(shí)就會(huì)根據(jù)指針p 所指向的具體數(shù)據(jù)類型來確定指針p 的數(shù)據(jù)類型。(運(yùn)行期間)
(2)類型識別的分類
1)靜態(tài)類型:變量(對象)自身的類型;在編譯階段就能確定所使用變量的數(shù)據(jù)類型。
?? 2)動(dòng)態(tài)類型:指針(引用)所指向?qū)ο蟮膶?shí)際類型;在運(yùn)行階段根據(jù)指針?biāo)赶虻木唧w數(shù)據(jù)類型來確定所使用的數(shù)據(jù)類型。
Base *b 所指向的實(shí)際對象無法確定,若指針b 指向的是子類對象,則程序正常運(yùn)行;若指針b 指向的是父類對象,則程序有可能出現(xiàn) Bug;
注:在 g++ 編譯器下上述情況均可正常運(yùn)行,但后者不建議使用;
? 在賦值兼容原則中,基類指針是否可以強(qiáng)制類型轉(zhuǎn)換為子類指針取決于動(dòng)態(tài)類型;(很重要?。。。?-- 只有動(dòng)態(tài)類型是子類對象才能進(jìn)行合法轉(zhuǎn)換
2、如何得到動(dòng)態(tài)類型
(1)利用多態(tài)
1)必須從基類開始提供類型虛函數(shù);
2)所有的派生類都必須重寫類型虛函數(shù);
3)每個(gè)派生類的類型 ID必須唯一;
結(jié)果:調(diào)用類型虛函數(shù)就可以知道當(dāng)前的對象究竟是什么類型,這樣就可以得到動(dòng)態(tài)類型,達(dá)到動(dòng)態(tài)類型識別效果;
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() // 類型虛函數(shù)
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 * 運(yùn)行結(jié)果: 85 * & =
0x7ffccf0dd85086 * I'm a Base. 87 * & = 0x7ffccf0dd860 88 * I'm a Derived. 89
* & = 0x7ffccf0dd87090 * I'm a Child. 91 */ 利用類型虛函數(shù)實(shí)現(xiàn)類型識別
?
?(2)利用 dynamic_cast
1)dynamic_cast這個(gè)關(guān)鍵字如果要轉(zhuǎn)換的實(shí)際類型和指定的類型不一樣,則會(huì)返回NULL。例如當(dāng)指定類型為子類對象時(shí),如果父類指針的動(dòng)態(tài)類型是這個(gè)子類對象時(shí),轉(zhuǎn)換成功,而動(dòng)態(tài)類型是父類對象或者其他子類對象時(shí),轉(zhuǎn)換失??;
2)dynamic_cast 要求使用的目標(biāo)對象類型必須是多態(tài),即:所在類族至少有一個(gè)虛函數(shù);
3)只能用于指針和引用之間的轉(zhuǎn)換
1.?用于指針轉(zhuǎn)換時(shí),轉(zhuǎn)換失敗,返回空指針;
2.?用于引用轉(zhuǎn)換時(shí),轉(zhuǎn)換失敗,將引發(fā) 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 只能確定最終的轉(zhuǎn)化結(jié)果,無法獲取動(dòng)態(tài)類型的原型 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 * 運(yùn)行結(jié)果: 73 * & = 0 74 * I'm a Base. 75 * & =
0x7ffccf0dd86076 * I'm a Derived. 77 * & = 0x7ffccf0dd870 78 * I'm a Child.
79 */ 利用 dynamic_cast 實(shí)現(xiàn)類型識別
?
?(3)利用 typeid(推薦這種方法)
1)typeid?是一個(gè)關(guān)鍵字,專門用于動(dòng)態(tài)類型識別;
2)typeid 關(guān)鍵字返回對應(yīng)參數(shù)的類型信息,此類型信息是一個(gè)type_info類對象;
1.?當(dāng)參數(shù)為類型時(shí),返回靜態(tài)類型信息;
2.?當(dāng)參數(shù)為變量時(shí):1>? ?參數(shù)變量內(nèi)部不存在虛函數(shù)表時(shí),返回靜態(tài)類型信息;??? 2>? ?參數(shù)變量內(nèi)部存在虛函數(shù)表時(shí),返回動(dòng)態(tài)類型信息;
3.?當(dāng)參數(shù)為 NULL 時(shí),將拋出異常;
? ? 3)typeid??使用時(shí)需要包含頭文件<typeinfo>;
4)typeid? 使用時(shí)直接指定對象或者類型。
5)typeid 在不同的編譯器內(nèi)部實(shí)現(xiàn)是不同的;
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
* 運(yùn)行結(jié)果: 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
* 5Child103 */ 利用 typeid 實(shí)現(xiàn)類型識別
? ? ?結(jié)論:
3 種動(dòng)態(tài)類型的實(shí)現(xiàn)方法 建議選 第3種 (typeid)。
對于多態(tài)實(shí)現(xiàn),存在以下缺陷:
1)必須從基類開始提供類型虛函數(shù);
? ? ? ?2)所有的派生類都必須重寫類型虛函數(shù);
? ? ? ? 3)每個(gè)派生類的類型名必須唯一;
對于 dynamic_cast 實(shí)現(xiàn),只能得到類型轉(zhuǎn)換的結(jié)果,不能獲取真正的動(dòng)態(tài)類型,同時(shí)?dynamic_cast 必須多態(tài)實(shí)現(xiàn)。
?
熱門工具 換一換