天天看點

static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

static_cast與dynamic_cast轉換

         static_cast與dynamic_cast轉換  

       

一 C語言中存在着兩種類型轉換:

隐式轉換和顯式轉換

隐式轉換:不同資料類型之間指派和運算,函數調用傳遞參數……編譯器完成

static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換
char ch;
int i = ch;      
static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

顯示轉換:在類型前增加 :(Type)變量 對變量進行的轉換。使用者顯式增加

static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換
char *pc = (char*)pb;
void *ps = (void*)pa;      
static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

二 C++中的類型轉換

  通過這兩種方式,C語言中大部分的類型轉換都可以順利進行。

至于能不能進行轉換,轉換後的結果如何,編譯器不管需要使用者自己去控制。

  C++繼承了C中的隐式和顯式轉換的方式。但這種轉換并不是安全和嚴格的,

加上C++本身對象模型的複雜性,C++增加了四個顯示轉換的關鍵字。(C++是強類型語言)

(static_cast,dynamic_cast,const_static,reinterpret_cast)

1 static_cast

(1)用于基本的資料類型轉換(char,int),及指針之間的轉換

static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換
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);      
static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

(2)類層次中基類與子類成員函數指針的轉換

static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換
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); //基類指向子類成員函數指針,必須進行轉換      
static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

(3)類層次結構中基類與子類指針或引用之間的轉換  

   上行轉換:子類指針或引用轉換成基類表示——安全

  下行轉換:基類指針或引用轉換成子類表示——危險(沒有動态類型檢查)

static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換
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 兩個無關聯之間轉換      
static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

2 dynamic_cast

(1)繼承關系的類指針對象或引用之間轉換

static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換
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 沒有關系 不是多态類型不能轉換      
static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

(2)包含有虛函數之間對象指針的轉換   

static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換
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 此處會發生一個運作時錯誤      
static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

         也就是說除了基類指針指向子類對象,可以沒有虛函數外,其它要進行dynamic_cast轉換必須具有虛函數才行。

那這是為什麼呢?下面繼續>

(3)dynam_cast轉換的安全性

         dynamic_cast是動态轉換,隻有在基類指針轉換為子類指針時才有意義。

(子類指針轉換為基類指針本來就是可以的:基類指針指向子類對象OK)。

但是基類指針轉換為子類指針,并不是每一次都有效:隻有基類指針本身指向的是一個派生類的對象,

然後将此基類指針轉換為對應的派生類指針才是有效的。這種情況在表面上是無法判定的。此時dynamic就發揮了作用。

情況1: static_cast轉換       

static_cast與dynamic_cast轉換 static_cast與dynamic_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沒有虛函數 不構成多态      
static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

情況2:     dynamic_cast轉換    

static_cast與dynamic_cast轉換 static_cast與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      
static_cast與dynamic_cast轉換 static_cast與dynamic_cast轉換

         dynamic_cast轉換不成功,則傳回0。

4 虛函數對于dynamic_cast轉換的作用

  為何使用dynamic_cast轉換類指針時,需要虛函數呢。

Dynamic_cast轉換是在運作時進行轉換,運作時轉換就需要知道類對象的資訊(繼承關系等)。

如何在運作時擷取到這個資訊——虛函數表。

  C++對象模型中,對象執行個體最前面的就是虛函數表指針,

通過這個指針可以擷取到該類對象的所有虛函數,包括父類的。

因為派生類會繼承基類的虛函數表,是以通過這個虛函數表,我們就可以知道該類對象的父類,在轉換的時候就可以用來判斷對象有無繼承關系。

  是以虛函數對于正确的基類指針轉換為子類指針是非常重要的。

轉自:http://www.cnblogs.com/bastard/archive/2011/12/14/2288117.html