一 C語言中存在著兩種類型轉(zhuǎn)換:
隱式轉(zhuǎn)換和顯式轉(zhuǎn)換
隱式轉(zhuǎn)換:不同數(shù)據(jù)類型之間賦值和運(yùn)算,函數(shù)調(diào)用傳遞參數(shù)……編譯器完成
顯示轉(zhuǎn)換:在類型前增加 :(Type)變量 對變量進(jìn)行的轉(zhuǎn)換。用戶顯式增加
char *pc = (char*)pb;
void *ps = (void*)pa;
二 C++中的類型轉(zhuǎn)換
通過這兩種方式,C語言中大部分的類型轉(zhuǎn)換都可以順利進(jìn)行。
至于能不能進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換后的結(jié)果如何,編譯器不管需要用戶自己去控制。
C++繼承了C中的隱式和顯式轉(zhuǎn)換的方式。但這種轉(zhuǎn)換并不是安全和嚴(yán)格的,
加上C++本身對象模型的復(fù)雜性,C++增加了四個顯示轉(zhuǎn)換的關(guān)鍵字。(C++是強(qiáng)類型語言)
(static_cast,dynamic_cast,const_static,reinterpret_cast)
1 static_cast
(1)用于基本的數(shù)據(jù)類型轉(zhuǎn)換(char,int),及指針之間的轉(zhuǎn)換
test_enum type = test_enum_1;
char a ;
int b = static_cast<int>(a);
char c = static_cast<char>(b);
type = static_cast<test_enum>(b);
char* pa = NULL;
int *pb = (int*)pa;
//int *pb = static_cast<int*>(pa); //error
//pa = static_cast<char*>(pb) //error
char *pc = (char*)pb;
//char *pc = static_cast<char*>(pb); //error
void *p = static_cast<void*>(pa);
pb = static_cast<int*>(p);
pc = static_cast<char*>(p);
(2)類層次中基類與子類成員函數(shù)指針的轉(zhuǎn)換
class A
{
public:
void set(){}
};
class B:public A
{
public:
void set(){}
};
typedef void (A::*PS_MFunc)(); //指向類A的成員函數(shù)指針
PS_MFunc func = &A::set;
func = static_cast<PS_MFunc>(&B::set); //基類指向子類成員函數(shù)指針,必須進(jìn)行轉(zhuǎn)換
(3)類層次結(jié)構(gòu)中基類與子類指針或引用之間的轉(zhuǎn)換
上行轉(zhuǎn)換:子類指針或引用轉(zhuǎn)換成基類表示——安全
下行轉(zhuǎn)換:基類指針或引用轉(zhuǎn)換成子類表示——危險(沒有動態(tài)類型檢查)
class A
{
};
class B:public A
{
};
class C:public A
{
};
class D
{
};
A objA;
B objB;
A* pObjA = new A();
B* pObjB = new B();
C* pObjC = new C();
D* pObjD = new D();
objA = static_cast<A&>(objB); //轉(zhuǎn)換為基類引用
objA = static_cast<A>(objB);
objB = static_cast<B>(objA); //error 不能進(jìn)行轉(zhuǎn)換
pObjA = pObjB; //right 基類指針指向子類對象
//objB = objA; //error 子類指針指向基類對象
pObjA = static_cast<A*>(pObjB); //right 基類指針指向子類
pObjB = static_cast<B*>(pObjA); //強(qiáng)制轉(zhuǎn)換 OK 基類到子類
//pObjC = static_cast<C*>(pObjB); //error 繼承于統(tǒng)一類的派生指針之間轉(zhuǎn)換
//pObjD = static_cast<D*>(pObjC); //error 兩個無關(guān)聯(lián)之間轉(zhuǎn)換
2 dynamic_cast
(1)繼承關(guān)系的類指針對象或引用之間轉(zhuǎn)換
class A
{
};
class B:public A
{
};
class C:public A
{
};
class D
{
};
A objA;
B objB;
A* pObjA = new A();
B* pObjB = new B();
C* pObjC = new C();
D* pObjD = new D();
//objA = dynamic_cast<A>(objB); //error 非引用
objA = dynamic_cast<A&>(objB);
//objB = dynamic_cast<B&>(objA); //error A 不是多態(tài)類型不能轉(zhuǎn)換 若有虛函數(shù)則可以進(jìn)行轉(zhuǎn)換
pObjA = dynamic_cast<A*>(pObjB);
//pObjB = dynamic_cast<B*>(pObjA); //error A 繼承關(guān)系 不是多態(tài)類型不能轉(zhuǎn)換
//pObjB = dynamic_cast<B*>(pObjC); //error C 兄弟關(guān)系 不是多態(tài)類型不能轉(zhuǎn)換
//pObjB = dynamic_cast<B*>(pObjD); //error D 沒有關(guān)系 不是多態(tài)類型不能轉(zhuǎn)換
(2)包含有虛函數(shù)之間對象指針的轉(zhuǎn)換
class A
{
Public:
Virtual ~A(){}
};
class B:public A
{
};
class C:public A
{
};
class D
{
Public:
Virtual ~D(){}
};
pObjB = dynamic_cast<B*>(pObjA); // worning 繼承關(guān)系 父類具有虛函數(shù) 多態(tài)
pObjB = dynamic_cast<B*>(pObjD); //worning 沒有關(guān)系 D是多態(tài)類型可以轉(zhuǎn)換
//以上結(jié)果:pObjB == NULL 此處會發(fā)生一個運(yùn)行時錯誤
也就是說除了基類指針指向子類對象,可以沒有虛函數(shù)外,其它要進(jìn)行dynamic_cast轉(zhuǎn)換必須具有虛函數(shù)才行。
那這是為什么呢?下面繼續(xù)>
(3)dynam_cast轉(zhuǎn)換的安全性
dynamic_cast是動態(tài)轉(zhuǎn)換,只有在基類指針轉(zhuǎn)換為子類指針時才有意義。
(子類指針轉(zhuǎn)換為基類指針本來就是可以的:基類指針指向子類對象OK)。
但是基類指針轉(zhuǎn)換為子類指針,并不是每一次都有效:只有基類指針本身指向的是一個派生類的對象,
然后將此基類指針轉(zhuǎn)換為對應(yīng)的派生類指針才是有效的。這種情況在表面上是無法判定的。此時dynamic就發(fā)揮了作用。
情況1: static_cast轉(zhuǎn)換
class A
{
};
class B:public A
{
public:
int m; //B 成員
};
A* pObjA = new A();
B* pObjB = NULL;
pObjB = static_cast<B*>(pObjA); //基類指針轉(zhuǎn)化為子類指針 成功轉(zhuǎn)換
pObjB->m = 10; //實(shí)際中pObj所指向的對象 是A類對象
//上面會發(fā)生什么呢,在VC6.0中正常運(yùn)行。。。?
//如果:
pObjB = dynamic_cast<B*>(pObjA); //error 基類A沒有虛函數(shù) 不構(gòu)成多態(tài)
情況2: dynamic_cast轉(zhuǎn)換
class A
{
public:
virtual ~A(){} //虛函數(shù) 多態(tài)
};
class B:public A
{
public:
int m;
};
A* pObjA = new A();
B* pObjB = NULL;
pObjB = dynamic_cast<B*>(pObjA); //編譯通過
//實(shí)際運(yùn)行結(jié)果:pObjB == NULL // dynamic_cast保證轉(zhuǎn)換無效 返回NULL
dynamic_cast轉(zhuǎn)換不成功,則返回0。
4 虛函數(shù)對于dynamic_cast轉(zhuǎn)換的作用
為何使用dynamic_cast轉(zhuǎn)換類指針時,需要虛函數(shù)呢。
Dynamic_cast轉(zhuǎn)換是在運(yùn)行時進(jìn)行轉(zhuǎn)換,運(yùn)行時轉(zhuǎn)換就需要知道類對象的信息(繼承關(guān)系等)。
如何在運(yùn)行時獲取到這個信息——虛函數(shù)表。
C++對象模型中,對象實(shí)例最前面的就是虛函數(shù)表指針,
通過這個指針可以獲取到該類對象的所有虛函數(shù),包括父類的。
因?yàn)榕缮悤^承基類的虛函數(shù)表,所以通過這個虛函數(shù)表,我們就可以知道該類對象的父類,在轉(zhuǎn)換的時候就可以用來判斷對象有無繼承關(guān)系。
所以虛函數(shù)對于正確的基類指針轉(zhuǎn)換為子類指針是非常重要的。