一 C語言中存在着兩種類型轉換:html
隱式轉換和顯式轉換安全
隱式轉換:不一樣數據類型之間賦值和運算,函數調用傳遞參數……編譯器完成函數
char ch; int i = ch;
顯示轉換:在類型前增長 :(Type)變量 對變量進行的轉換。用戶顯式增長spa
char *pc = (char*)pb; void *ps = (void*)pa;
二 C++中的類型轉換指針
經過這兩種方式,C語言中大部分的類型轉換均可以順利進行。code
至於能不能進行轉換,轉換後的結果如何,編譯器無論須要用戶本身去控制。htm
C++繼承了C中的隱式和顯式轉換的方式。但這種轉換並非安全和嚴格的,對象
加上C++自己對象模型的複雜性,C++增長了四個顯示轉換的關鍵字。(C++是強類型語言)blog
(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++對象模型中,對象實例最前面的就是虛函數表指針,
經過這個指針能夠獲取到該類對象的全部虛函數,包括父類的。
由於派生類會繼承基類的虛函數表,因此經過這個虛函數表,咱們就能夠知道該類對象的父類,在轉換的時候就能夠用來判斷對象有無繼承關係。
因此虛函數對於正確的基類指針轉換爲子類指針是很是重要的。