標籤:stream names 帶來 class 類型 oid 目標 地址 snippet
參考:http://www.weixueyuan.net/view/6377.html
總結:
產生這種運行期的錯誤原因在於static_cast強制類型轉換時並不具有保證型別安全的功能,而C++提供的dynamic_cast卻能解決這一問題,dynamic_cast可以在程式運行時檢測類型轉換是否型別安全。
當然dynamic_cast使用起來也是有條件的,它要求所轉換的運算元必須包含多態類類型(即至少包含一個虛函數的類)。
總結一下dynamic_cast轉換規則,只允許指向衍生類別對象的指標轉換為指向基類對象的指標。
C++提供的兩個類型轉換操作符static_cast和dynamic_cast,static_cast可以用於任何類型的強制類型轉換,但是它不保證轉換過程中的型別安全,dynamic_cast只能用於多態類類型的轉換,而且要求轉換的目的類型必須為指標或引用,並且它可以保證轉換過程中型別安全。
任意兩個不相關的多態類類型之間的轉換也是不能進行的。(沒有繼承關係的)
在C++中,編譯期的類型轉換有可能會在運行時出現錯誤,特別是涉及到類對象的指標或引用操作時,更容易產生錯誤。Dynamic_cast操作符則可以在運行期對可能產生問題的類型轉換進行測試。
例1:
#include<iostream>using namespace std;class base{public : void m(){cout<<"m"<<endl;}};class derived : public base{public: void f(){cout<<"f"<<endl;}};int main(){ derived * p; p = new base; p = static_cast<derived *>(new base); p->m(); p->f(); return 0;}
本例中定義了兩個類:base類和derived類,這兩個類構成繼承關係。在base類中定義了m函數,derived類中定義了f函數。在前面介紹多態時,我們一直是用基類指標指向衍生類別或基類對象,而本例則不同了。本例主函數中定義的是一個衍生類別指標,當我們將其指向一個基類對象時,這是錯誤的,會導致編譯錯誤。但是通過強制類型轉換我們可以將衍生類別指標指向一個基類對象,p = static_cast<derived *>(new base);語句實現的就是這樣一個功能,這樣的一種強制類型轉換時合乎C++文法規定的,但是是非常不明智的,它會帶來一定的危險。在程式中p是一個衍生類別對象,我們將其強制指向一個基類對象,首先通過p指標調用m函數,因為基類中包含有m函數,這一句沒有問題,之後通過p指標調用f函數。一般來講,因為p指標是一個衍生類別類型的指標,而衍生類別中擁有f函數,因此p->f();這一語句不會有問題,但是本例中p指標指向的確實基類的對象,而基類中並沒有聲明f函數,雖然p->f();這一語句雖然仍沒有語法錯誤,但是它卻產生了一個運行時的錯誤。換言之,p指標是衍生類別指標,這表明程式設計人員可以通過p指標調用衍生類別的成員函數f,但是在實際的程式設計過程中卻誤將p指標指向了一個基類對象,這就導致了一個運行期錯誤。
產生這種運行期的錯誤原因在於static_cast強制類型轉換時並不具有保證型別安全的功能,而C++提供的dynamic_cast卻能解決這一問題,dynamic_cast可以在程式運行時檢測類型轉換是否型別安全。當然dynamic_cast使用起來也是有條件的,它要求所轉換的運算元必須包含多態類類型(即至少包含一個虛函數的類)。
例2:
#include<iostream>using namespace std;class base{public : void m(){cout<<"m"<<endl;}};class derived : public base{public: void f(){cout<<"f"<<endl;}};int main(){ derived * p; p = new base; p = dynamic_cast<derived *>(new base); p->m(); p->f(); return 0;}
在本例中利用dynamic_cast進行強制類型轉換,但是因為base類中並不存在虛函數,因此p = dynamic_cast<derived *>(new base);這一句會編譯錯誤。dynamic_cast能否正確轉換與目標類型是否為多態類類型無關,dynamic_cast要求被轉換的類型必須為多態類類型。為瞭解決本例中的語法錯誤,我們可以將base類中的函數m聲明為虛函數,virtual void m(){cout<<"m"<<endl;}。
dynamic_cast還要求<>內部所描述的目標類型必須為指標或引用。如例3所示,如果我們將例2中的主函數換成例3的形式,這也是無法通過編譯的。
例3:
int main(){ base b; dynamic_cast<derived>(b); return 0;}
我們來看一下正確使用dynamic_cast的代碼。
例4:
#include<iostream>using namespace std;class base{public : virtual void m(){cout<<"m"<<endl;}};class derived : public base{public: void f(){cout<<"f"<<endl;}};int main(){ derived * p; p = dynamic_cast<derived *>(new base); if(p) { p->m(); p->f(); } else cout<<"Convert not safe!"<<endl; return 0;}
在本例中通過dynamic_cast來初始化指標p,在初始化過程中dynamic_cast會檢測運算元new base轉換為目標類型derived *是否能保證型別安全,如果型別安全則將new base結果賦給p指標,否則返回0,也即false。而本例中是要用基類對象地址去初始化衍生類別指標,這顯然是無法保證型別安全的,因此p最後得到的傳回值是0。在主函數中經過判斷語句,最終程式輸出“Convert not safe!”。
Dynamic_cast轉換有自己的規則,下面將通過樣本來介紹轉換規則。
例4:
#include<iostream>using namespace std;class base{public : virtual void m(){cout<<"m"<<endl;}};class derived : public base{public: virtual void f(){cout<<"f"<<endl;}};int main(){ derived * d; d = dynamic_cast<derived *>(new base); if(d) { cout<<"Base to Derived is ok"<<endl; delete d; } else cout<<"Base to Derived is error"<<endl; base * b; b = dynamic_cast<base *>(new derived); if(b) { cout<<"Derived to Base is ok"<<endl; delete b; } else cout<<"Derived to Base is error"<<endl; return 0;}
本例分別定義了兩個類:base類和derived類,這兩個類構成繼承關係,為了測試dynamic_cast轉換規則,我們在類中各自訂了一個虛函數。在本例的主函數中我們分別測試基類轉換為衍生類別和衍生類別轉換為基類時dynamic_cast轉換傳回值。本例最終運行結果如下:
Base to Derived is error
Derived to Base is ok
從結果可以看出從不能將指向基類對象的指標轉換為指向衍生類別對象的指標,但是可以將指向衍生類別對象的指標轉換為指向基類對象的指標。
例5:
#include<iostream>using namespace std;class A{public : virtual void m(){cout<<"m"<<endl;}};class B{public: virtual void f(){cout<<"f"<<endl;}};int main(){ A * a; a = dynamic_cast<A *>(new B); if(a) { cout<<"B to A is ok"<<endl; delete a; } else cout<<"B to A is error"<<endl; B * b; b = dynamic_cast<B *>(new A); if(b) { cout<<"A to B is ok"<<endl; delete b; } else cout<<"A to B is error"<<endl; return 0;}
在本例中,定義了兩個類A和B,這兩個類不構成繼承關係,我們嘗試將指向兩個類對象的指標進行互相轉換,看程式運行結果:
B to A is error
A to B is error
從程式運行結果不難看出,任意兩個不相關的多態類類型之間的轉換也是不能進行的。
總結一下dynamic_cast轉換規則,只允許指向衍生類別對象的指標轉換為指向基類對象的指標。
C++提供的兩個類型轉換操作符static_cast和dynamic_cast,static_cast可以用於任何類型的強制類型轉換,但是它不保證轉換過程中的型別安全,dynamic_cast只能用於多態類類型的轉換,而且要求轉換的目的類型必須為指標或引用,並且它可以保證轉換過程中型別安全。
4.7 C++ dynamic_cast操作符