詳解c++中的類型識(shí)別
1、類型識(shí)別的相關(guān)概念
(1)類型識(shí)別的作用
類型識(shí)別是面向?qū)ο笾幸氲囊粋€(gè)新概念,主要用來(lái)判斷賦值兼容性原則中的類型問(wèn)題,即此時(shí)的數(shù)據(jù)類型到底是基類類型還是派生類類型?
當(dāng)基類指針指向子類對(duì)象 或者基類引用成為子類對(duì)象的別名 時(shí),就需要使用類型識(shí)別;
Base *p = new Derived(); Base &r = *p
對(duì)于上面的語(yǔ)句,我們可以這樣認(rèn)識(shí),指針p是Base類型,但是P 又指向了一個(gè)新的Derived類型,此時(shí)很難判斷指針P 的數(shù)據(jù)類型;同理,引用r 本來(lái)作為父類的別名而存在,但由于賦值兼容性,引用r也可以作為子類的別名,同樣此時(shí) 引用 r 的數(shù)據(jù)類型也不能確定;
注:1)由之前所學(xué)知識(shí),若沒(méi)有虛函數(shù)重寫,編譯器為了安全起見(jiàn),會(huì)將指針p 當(dāng)作 Base 類型;(編譯期間)
2)若有虛函數(shù)重寫,就會(huì)發(fā)生動(dòng)態(tài)多態(tài)特性,此時(shí)就會(huì)根據(jù)指針p 所指向的具體數(shù)據(jù)類型來(lái)確定指針p 的數(shù)據(jù)類型。(運(yùn)行期間)
(2)類型識(shí)別的分類
1)靜態(tài)類型:變量(對(duì)象)自身的類型;在編譯階段就能確定所使用變量的數(shù)據(jù)類型。
2)動(dòng)態(tài)類型:指針(引用)所指向?qū)ο蟮膶?shí)際類型;在運(yùn)行階段根據(jù)指針?biāo)赶虻木唧w數(shù)據(jù)類型來(lái)確定所使用的數(shù)據(jù)類型。
Base *b 所指向的實(shí)際對(duì)象無(wú)法確定,若指針b 指向的是子類對(duì)象,則程序正常運(yùn)行;若指針b 指向的是父類對(duì)象,則程序有可能出現(xiàn) Bug;
注:在 g++ 編譯器下上述情況均可正常運(yùn)行,但后者不建議使用;
在賦值兼容原則中,基類指針是否可以強(qiáng)制類型轉(zhuǎn)換為子類指針取決于動(dòng)態(tài)類型;(很重要!?。。?-- 只有動(dòng)態(tài)類型是子類對(duì)象才能進(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)前的對(duì)象究竟是什么類型,這樣就可以得到動(dòng)態(tài)類型,達(dá)到動(dòng)態(tài)類型識(shí)別效果;
利用類型虛函數(shù)實(shí)現(xiàn)類型識(shí)別
#include <iostream> #include <string> using namespace std; class Base { public: enum { ID = 0 }; virtual int type() // 類型虛函數(shù) { return ID; } }; class Derived : public Base { public: enum { ID = 1 }; int type() { return ID; } void print() { cout << "I'm a Derived. " << endl; } }; class Child : public Base { public: enum { ID = 2 }; int type() { return ID; } }; void test(Base* pb) { if( pb->type() == Child::ID ) { Child* pc = static_cast<Child*>(pb); //Child* pc = dynamic_cast<Child*>(pb); // 同上 cout << "& = " << pc << endl; cout << "I'm a Child. " << endl; } if( pb->type() == Derived::ID ) { Derived* pd = static_cast<Derived*>(pb); //Derived* pd = dynamic_cast<Derived*>(pb); // 同上 cout << "& = " << pd << endl; pd->print(); } if( pb->type() == Base::ID ) { cout << "& = " << pb << endl; cout << "I'm a Base. " << endl; } } int main(int argc, char *argv[]) { Base b; Derived d; Child c; test(&b); test(&d); test(&c); return 0; } /** * 運(yùn)行結(jié)果: * & = 0x7ffccf0dd850 * I'm a Base. * & = 0x7ffccf0dd860 * I'm a Derived. * & = 0x7ffccf0dd870 * I'm a Child. */
(2)利用 dynamic_cast
1)dynamic_cast這個(gè)關(guān)鍵字如果要轉(zhuǎn)換的實(shí)際類型和指定的類型不一樣,則會(huì)返回NULL。例如當(dāng)指定類型為子類對(duì)象時(shí),如果父類指針的動(dòng)態(tài)類型是這個(gè)子類對(duì)象時(shí),轉(zhuǎn)換成功,而動(dòng)態(tài)類型是父類對(duì)象或者其他子類對(duì)象時(shí),轉(zhuǎn)換失??;
2)dynamic_cast 要求使用的目標(biāo)對(duì)象類型必須是多態(tài),即:所在類族至少有一個(gè)虛函數(shù);
3)只能用于指針和引用之間的轉(zhuǎn)換
1.用于指針轉(zhuǎn)換時(shí),轉(zhuǎn)換失敗,返回空指針;
2.用于引用轉(zhuǎn)換時(shí),轉(zhuǎn)換失敗,將引發(fā) bad_cast異常。
#include <iostream> #include <string> using namespace std; class Base { public: virtual ~Base() { } }; class Derived : public Base { public: void print() { cout << "I'm a Derived. " << endl; } }; class Child : public Base { }; void test(Base* pb) { // dynamic_cast 只能確定最終的轉(zhuǎn)化結(jié)果,無(wú)法獲取動(dòng)態(tài)類型的原型 Derived* pd = dynamic_cast<Derived*>(pb); if(pd != NULL) { // Derived 類類型, 可以使用指針pd訪問(wèn)Derived類的成員 cout << "& = " << pd << endl; pd->print(); } else { Child* pc = dynamic_cast<Child*>(pb); if(pc != NULL) { // Child 類類型, 可以使用指針pc訪問(wèn)Child類的成員 cout << "& = " << pc << endl; cout << "I'm a Child. " << endl; } else { // Base 類類型, 可以使用指針pb訪問(wèn)Base類的成員 cout << "& = " << pc << endl; cout << "I'm a Base. " << endl; } } } int main(int argc, char *argv[]) { Base b; Derived d; Child c; test(&b); test(&d); test(&c); return 0; } /** * 運(yùn)行結(jié)果: * & = 0 * I'm a Base. * & = 0x7ffccf0dd860 * I'm a Derived. * & = 0x7ffccf0dd870 * I'm a Child. */
(3)利用 typeid(推薦這種方法)
1)typeid是一個(gè)關(guān)鍵字,專門用于動(dòng)態(tài)類型識(shí)別;
2)typeid 關(guān)鍵字返回對(duì)應(yīng)參數(shù)的類型信息,此類型信息是一個(gè)type_info類對(duì)象;
1.當(dāng)參數(shù)為類型時(shí),返回靜態(tài)類型信息;
2.當(dāng)參數(shù)為變量時(shí):1> 參數(shù)變量?jī)?nèi)部不存在虛函數(shù)表時(shí),返回靜態(tài)類型信息; 2> 參數(shù)變量?jī)?nèi)部存在虛函數(shù)表時(shí),返回動(dòng)態(tài)類型信息;
3.當(dāng)參數(shù)為 NULL 時(shí),將拋出異常;
3)typeid使用時(shí)需要包含頭文件<typeinfo>;
4)typeid 使用時(shí)直接指定對(duì)象或者類型。
5)typeid 在不同的編譯器內(nèi)部實(shí)現(xiàn)是不同的;
int i = 0; const type_info& tiv = typeid(i); // 將 i 的類型信息放到 type_info 中去; const type_info& tii = typeid(int); cout << (tiv == tii) << endl; // 1
利用 typeid 實(shí)現(xiàn)類型識(shí)別
#include <iostream> #include <string> #include <typeinfo> using namespace std; class Base { public: virtual ~Base() { } }; class Derived : public Base { public: void print() { cout << "I'm a Derived." << endl; } }; class Child : public Base { public: void print() { cout << "I'm a Child." << endl; } }; void test(Base* pb) { const type_info& tb = typeid(*pb); if( tb == typeid(Derived) ) { Derived* pd = dynamic_cast<Derived*>(pb); cout << "& = " << pd << endl; pd->print(); } else if( tb == typeid(Child) ) { Child* pc = dynamic_cast<Child*>(pb); cout << "& = " << pc << endl; pc->print(); } else if( tb == typeid(Base) ) { cout << "& = " << pb << endl; cout << "I'm a Base. " << endl; } cout << tb.name() << endl; } int main(int argc, char *argv[]) { Base b; Derived d; Child c; int index; char ch; const type_info& tp = typeid(b); const type_info& tc = typeid(d); const type_info& tn = typeid(c); const type_info& ti = typeid(index); const type_info& tch = typeid(ch); cout<<tp.name()<<endl; cout<<tc.name()<<endl; cout<<tn.name()<<endl; cout<<ti.name()<<endl; cout<<tch.name()<<endl; test(&b); test(&d); test(&c); return 0; } /** * 運(yùn)行結(jié)果: * 4Base * 7Derived * 5Child * i * c * & = 0x7ffcbd4d6280 * I'm a Base. * 4Base * & = 0x7ffcbd4d6290 * I'm a Derived. * 7Derived * & = 0x7ffcbd4d62a0 * I'm a Child. * 5Child */
結(jié)論:
3 種動(dòng)態(tài)類型的實(shí)現(xiàn)方法 建議選 第3種 (typeid)。
對(duì)于多態(tài)實(shí)現(xiàn),存在以下缺陷:
1)必須從基類開始提供類型虛函數(shù);
2)所有的派生類都必須重寫類型虛函數(shù);
3)每個(gè)派生類的類型名必須唯一;
對(duì)于 dynamic_cast 實(shí)現(xiàn),只能得到類型轉(zhuǎn)換的結(jié)果,不能獲取真正的動(dòng)態(tài)類型,同時(shí)dynamic_cast 必須多態(tài)實(shí)現(xiàn)。
到此這篇關(guān)于 詳解c++中的類型識(shí)別的文章就介紹到這了,更多相關(guān)c++ 類型識(shí)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS調(diào)用C++函數(shù)拋出異常及捕捉異常詳解
這篇文章主要介紹了js調(diào)用C++函數(shù)的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2021-08-08Windows10配置VSCode C++環(huán)境(超詳細(xì),面向小白以及大佬們)
這篇文章主要介紹了Windows10配置VSCode C++環(huán)境(超詳細(xì),面向小白以及大佬們),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12C++?電話號(hào)碼的字母組合功能實(shí)現(xiàn)
這篇文章主要介紹了C++?電話號(hào)碼的字母組合,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08C語(yǔ)言中的字符串?dāng)?shù)據(jù)在C中的存儲(chǔ)方式
這篇文章主要介紹了C語(yǔ)言中的字符串?dāng)?shù)據(jù)在C中的存儲(chǔ)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07