c++ 相容 c,對於c語言的型別轉換也是支援的。但是c風格的型別轉換有很多缺點,它可以任意型別之間進行轉換,查詢源型別比較麻煩,而且有的型別之間進行轉換是不安全的行為。c++的設計強調型別安全,為了修補這個大坑,c++提供了四種的型別轉換:static_cast,const_cast,dynamic_cast,reinterpret_cast。各個轉型動作專門用於不同的方面,這樣目標越窄化,編譯器可能診斷出錯誤的運用。
對於c的寫碼形式為:
(type) expression
對於c++寫碼形式為:
static_cast( expression )
const_cast( expression )
....
下面就對這四種型別轉換進行整理。
任何具有明確定義的型別轉換,和c的轉換相同,以及相同的限制。比如:不能將乙個struct轉換成int,或將double轉換成指標,也無法移除表示式的常量性(那是const_cast做的事情)。舉幾個例子:
int a = 1;
double b = 3.5;
a = static_cast( b );
b = static_cast( a );
static_cast是適用範圍最廣的轉換,後面的三個適用於更窄的目的。
用來改變表示式中的常量性或易變性。一旦我們對某個物件使用了const_cast,編譯器就不在阻止我們對該物件的寫操作。如:
const char* pc;
char* p = const_cast( pc ); //可以通過p寫值了
int a = 3;
const int* pint = &a;
int* pint2 = const_cast(pint);
*pint2 = 4;
cout << *pint << " " << *pint2 << endl; //輸出 4 4
如果將const_cast應用於其它用途,那麼轉型動作會被拒絕。同樣的只有const_cast能改變表示式的常量屬性,使用其它c++形式的強制型別轉換會引發編譯器錯誤。
但需要特別注意的是const_cast不是用於去除變數的常量性,而是去除指向常數物件的指標或引用的常量性。
const int a = 10;
int b = const_cast(a); //編譯錯誤:
const_cast只能去除頂層const屬性,比如:
const char* const pchar = "abcd";
char* pchar2 = const_cast(pchar);
pchar2 = 'b'; //執行時崩潰,只是去除了頂層const,pchar2相當於char* const pchar2 =.... 不能改變指向
用來執行繼承體系中安全的向下轉型動作。即將基類的指標或引用安全的轉換成派生類的指標或引用。
需要注意的是轉換的基類中需要有虛函式。因為通過之前對虛函式的整理,我們知道型別資訊儲存在虛函式表中。
指標型別的dynamic_cast轉換失敗後,會返回空指標。我們判斷返回值來保證**安全。例:
if( derived* pd = dynamic_cast( pb))
引用型別的dynamic_cast因為引用不存在空引用,所以引用型別的轉換失敗後會拋出名為 std::bad_cast 的異常。我們對異常進行捕捉來保證**安全。如:
void f( base& b)
catch ( bad_cast )
}
dynamic_cast是唯一在執行時處理的,要進行型別檢查,所以效率會比較低,盡量少使用。
用來為運算物件的位模式提供較低層次上的重新解釋,不會改變其值。簡單點講有點像對型別的重新解釋,一般用於指標、引用。例:
int* pi;
char* pc = reinterpret_cast( pi );
我們要牢記pc所指的真實型別,否則使用不當,會引發執行時錯誤。例如:
string str( pc );
使用reinterpret_cast強制轉換過程僅僅只是位元位的拷貝,因此在使用過程中需要特別謹慎。有乙個笑話說為啥這個轉換名稱這麼長?因為設計者也不想讓你使用它,所以把它的名字設計的這麼複雜。
許多人相信,轉型其實什麼都沒做,只是告訴編譯器把某種新型別視為另一種型別。但其實是錯誤的。
比如:
int x,y;
...double d = static_cast(x)/y;
將 int x 轉型為 double 幾乎肯定會產生一些**,因為在大部分計算機體系結構中,int的底層表述不同於double底層表述。
還有:
class base;
class derived : public base ;
derived d;
base* pb = &d; //隱喻地將derived* 轉換為 base*
對於派生類的物件,可能擁有乙個以上的位址。之前整理的虛函式及多型原理中講述過這個原理。在多重繼承時,會有多個虛函式表,而轉換為不同的基類時,要對虛指標進行偏移操作。而不同的物件布局方式隨著編譯器的不同而不同,有可能在乙個平台行得通,而在其它平台就行不通。
還有一點是dynamic_cast執行的速度是非常慢的。例如至少有乙個很普通的實現版本基於「class名稱之字串比較」,如果你在四層深的單繼承體系內的某個物件上執行 dynamic_cast ,剛才說的那個實現版本可能會耗用四次strcmp呼叫,用以比較class名稱。深度繼承或多重繼承的成本更高。
使用dynamic_cast的情況是,你有乙個指向base的指標或引用,你只能靠它來處理物件,但是你又想呼叫派生類中的函式。最好的辦法就是把處理的函式寫成虛函式,這樣直接使用指向base的指標或引用就可呼叫。
所以可以總結為:
如果可以,盡量避免轉型,特別是注重效率的**中避免dynamic_cast。如果有個設計需要轉型動作,試著發展無須轉型的替代設計。
如果轉型是必要的,試著將它隱藏於某個函式背後。客戶隨後可以呼叫該函式,而不需要將轉型放進他們自己的**中。
寧可使用c++ 新式轉型,不要使用舊式轉型。前者很容易辨識出來,而且也比較有著分門別類的職能。
感謝大家,我是假裝很努力的youngyangd(小羊)
參考資料:
《effective c++》
《c++ primer 第五版》
《more effective c++》
C 強制型別轉換
四種型別可能很多人都常常忽略就象我一樣,但是有時還是比較有用的。不了解的建議看看,一些機制我也不是十分了解,只是將一些用法寫出來讓大家看看。強制轉化無論從語法還是語意上看,都是c 中最難看的特徵之一。但是基於c風格的轉化的語義的不明確性及其一些潛在問題。強制型別轉化最終還是被c 接受了。1.stat...
C 強制型別轉換
標準c 中主要有四種強制轉換型別運算子 const cast,reinterpret cast,static cast,dynamic cast等等。1 static cast a 將位址a轉換成型別t,t和a必須是指標 引用 算術型別或列舉型別。表示式static cast a a的值轉換為模板中...
C 強制型別轉換
關於強制型別轉換的問題,很多書都討論過,寫的最詳細的是c 之父的 c 的設計和演化 最好的解決方法就是不要使用c風格的強制型別轉換,而是使用標準c 的型別轉換符 static cast,dynamic cast。標準c 中有四個型別轉換符 static cast,dynamic cast,reint...