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