static_cast與dynamic_cast轉換
static_cast與dynamic_cast轉換
一 C語言中存在着兩種類型轉換:
隐式轉換和顯式轉換
隐式轉換:不同資料類型之間指派和運算,函數調用傳遞參數……編譯器完成
char ch;
int i = ch;
顯示轉換:在類型前增加 :(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++對象模型中,對象執行個體最前面的就是虛函數表指針,
通過這個指針可以擷取到該類對象的所有虛函數,包括父類的。
因為派生類會繼承基類的虛函數表,是以通過這個虛函數表,我們就可以知道該類對象的父類,在轉換的時候就可以用來判斷對象有無繼承關系。
是以虛函數對于正确的基類指針轉換為子類指針是非常重要的。
轉自:http://www.cnblogs.com/bastard/archive/2011/12/14/2288117.html