詳解c++中的類型識別
1、類型識別的相關(guān)概念
(1)類型識別的作用
類型識別是面向?qū)ο笾幸氲囊粋€新概念,主要用來判斷賦值兼容性原則中的類型問題,即此時的數(shù)據(jù)類型到底是基類類型還是派生類類型?
當基類指針指向子類對象 或者基類引用成為子類對象的別名 時,就需要使用類型識別;
Base *p = new Derived(); Base &r = *p
對于上面的語句,我們可以這樣認識,指針p是Base類型,但是P 又指向了一個新的Derived類型,此時很難判斷指針P 的數(shù)據(jù)類型;同理,引用r 本來作為父類的別名而存在,但由于賦值兼容性,引用r也可以作為子類的別名,同樣此時 引用 r 的數(shù)據(jù)類型也不能確定;
注:1)由之前所學知識,若沒有虛函數(shù)重寫,編譯器為了安全起見,會將指針p 當作 Base 類型;(編譯期間)
2)若有虛函數(shù)重寫,就會發(fā)生動態(tài)多態(tài)特性,此時就會根據(jù)指針p 所指向的具體數(shù)據(jù)類型來確定指針p 的數(shù)據(jù)類型。(運行期間)
(2)類型識別的分類

1)靜態(tài)類型:變量(對象)自身的類型;在編譯階段就能確定所使用變量的數(shù)據(jù)類型。
2)動態(tài)類型:指針(引用)所指向?qū)ο蟮膶嶋H類型;在運行階段根據(jù)指針所指向的具體數(shù)據(jù)類型來確定所使用的數(shù)據(jù)類型。

Base *b 所指向的實際對象無法確定,若指針b 指向的是子類對象,則程序正常運行;若指針b 指向的是父類對象,則程序有可能出現(xiàn) Bug;
注:在 g++ 編譯器下上述情況均可正常運行,但后者不建議使用;
在賦值兼容原則中,基類指針是否可以強制類型轉(zhuǎn)換為子類指針取決于動態(tài)類型;(很重要!?。。?-- 只有動態(tài)類型是子類對象才能進行合法轉(zhuǎn)換
2、如何得到動態(tài)類型
(1)利用多態(tài)
1)必須從基類開始提供類型虛函數(shù);
2)所有的派生類都必須重寫類型虛函數(shù);
3)每個派生類的類型 ID必須唯一;
結(jié)果:調(diào)用類型虛函數(shù)就可以知道當前的對象究竟是什么類型,這樣就可以得到動態(tài)類型,達到動態(tài)類型識別效果;
利用類型虛函數(shù)實現(xiàn)類型識別
#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;
}
/**
* 運行結(jié)果:
* & = 0x7ffccf0dd850
* I'm a Base.
* & = 0x7ffccf0dd860
* I'm a Derived.
* & = 0x7ffccf0dd870
* I'm a Child.
*/
(2)利用 dynamic_cast
1)dynamic_cast這個關(guān)鍵字如果要轉(zhuǎn)換的實際類型和指定的類型不一樣,則會返回NULL。例如當指定類型為子類對象時,如果父類指針的動態(tài)類型是這個子類對象時,轉(zhuǎn)換成功,而動態(tài)類型是父類對象或者其他子類對象時,轉(zhuǎn)換失??;
2)dynamic_cast 要求使用的目標對象類型必須是多態(tài),即:所在類族至少有一個虛函數(shù);
3)只能用于指針和引用之間的轉(zhuǎn)換
1.用于指針轉(zhuǎn)換時,轉(zhuǎn)換失敗,返回空指針;
2.用于引用轉(zhuǎn)換時,轉(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é)果,無法獲取動態(tài)類型的原型
Derived* pd = dynamic_cast<Derived*>(pb);
if(pd != NULL)
{
// Derived 類類型, 可以使用指針pd訪問Derived類的成員
cout << "& = " << pd << endl;
pd->print();
}
else
{
Child* pc = dynamic_cast<Child*>(pb);
if(pc != NULL)
{
// Child 類類型, 可以使用指針pc訪問Child類的成員
cout << "& = " << pc << endl;
cout << "I'm a Child. " << endl;
}
else
{
// Base 類類型, 可以使用指針pb訪問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;
}
/**
* 運行結(jié)果:
* & = 0
* I'm a Base.
* & = 0x7ffccf0dd860
* I'm a Derived.
* & = 0x7ffccf0dd870
* I'm a Child.
*/
(3)利用 typeid(推薦這種方法)
1)typeid是一個關(guān)鍵字,專門用于動態(tài)類型識別;
2)typeid 關(guān)鍵字返回對應(yīng)參數(shù)的類型信息,此類型信息是一個type_info類對象;
1.當參數(shù)為類型時,返回靜態(tài)類型信息;
2.當參數(shù)為變量時:1> 參數(shù)變量內(nèi)部不存在虛函數(shù)表時,返回靜態(tài)類型信息; 2> 參數(shù)變量內(nèi)部存在虛函數(shù)表時,返回動態(tài)類型信息;
3.當參數(shù)為 NULL 時,將拋出異常;
3)typeid使用時需要包含頭文件<typeinfo>;
4)typeid 使用時直接指定對象或者類型。
5)typeid 在不同的編譯器內(nèi)部實現(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 實現(xiàn)類型識別
#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;
}
/**
* 運行結(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 種動態(tài)類型的實現(xiàn)方法 建議選 第3種 (typeid)。
對于多態(tài)實現(xiàn),存在以下缺陷:
1)必須從基類開始提供類型虛函數(shù);
2)所有的派生類都必須重寫類型虛函數(shù);
3)每個派生類的類型名必須唯一;
對于 dynamic_cast 實現(xiàn),只能得到類型轉(zhuǎn)換的結(jié)果,不能獲取真正的動態(tài)類型,同時dynamic_cast 必須多態(tài)實現(xiàn)。
到此這篇關(guān)于 詳解c++中的類型識別的文章就介紹到這了,更多相關(guān)c++ 類型識別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解C++設(shè)計模式編程中建造者模式的實現(xiàn)
這篇文章主要介紹了C++設(shè)計模式編程中建造者模式的實現(xiàn),建造者模式將一個復雜對象的構(gòu)建于它的表現(xiàn)分離,可以減少代碼冗余,需要的朋友可以參考下2016-03-03

