C++中顯示轉換也成為強制類型轉換(cast),有四種:static_cast、dynamic_cast、const_cast、reinterpret_cast。命名的強制類型轉換符號一般形式如下:
cast_name<type>(expression);
以下分別介紹
一、static_cast
任何具有明確定義的類型轉換,只要不包含底層const都可以使用static_cast。好吧這句話我不是很懂,換句話:編譯器隱式執行的任何類型轉換都可以由static_cast顯示完成。也就是說,兩類型之間可以發生隱式的轉換,就可以用static_cast顯示轉換,有點意思。但要知道的是C++基本類型的指針之間不含有隱式轉換(void*除外、const的有些也是可以的),需要顯示轉換。什么意思?如下:
double d=3.14; int i=d; //編譯器的隱式轉換,等價於下面這條語句 int i=static_cast<int>(d); /*指針之間的轉換*/ char str[]="good"; char *ptr=str; int *p=static_cast<int *>(ptr); //編譯錯誤,兩者之間的轉換要顯式,如下 int *p=(int *)(ptr);
僅當類型之間可隱式轉換時(除類層次見的下行轉換以外),static_cast的轉換才是合法的,否則將出錯。(基類指針或引用轉換成子類指針或引用為下行轉換)
類層次間的下行轉換不能通過隱式轉換完成,但是可以通過static_cast完成,但是由於沒有動態類型檢查,所以是不安全的。(至於這個不安全,我們會在dynamic_cast中具體說)。
class Base{};
class child:public Base{};
Base b;
child c;
c=static_cast<child *>(b); //下行轉換,正確;
c=b; //編譯錯誤
二、const_cast
只用使用const_cast才能將const性質轉換掉。在這種情況下,試圖使用其他三種形式的強制轉換都會導致編譯時的錯誤。類似地,除了添加或者刪除const特性,用const_cast符來執行其他任何類型轉換,都會引起編譯錯誤。
const double val=3.14; double *ptr=NULL; /*為了使ptr指向val,使用const_cast*/ ptr=const_cast<double *>(&val);
在《C++ primer》(第五版)中是這樣介紹const_cast的:
const_cast只能改變運算對象的底層const
const char *pc; char *p=const_cast<char*>(pc);//正確但是通過p寫值是未定義的行為
對於將常量對象轉換成非常量對象的行為,我們一般稱其為“去掉const性質(cast away the const)”。一旦我們去掉了某個對象的const性質,編譯器就不再阻止我們對該對象進行寫操作了。如果對象本身不是一個常量,使用強制類型轉換獲得寫權限是合法的行為。然而如果對象是一個常量,再使用const_cast執行寫操作就會產生未定義的后果。
只有const_cast能改變表達式的常量屬性,使用其他形式的命名強制類型轉換改變表達式的常量屬性都將引發編譯器錯誤。同樣的,也不能用const_cast改變表達式的 類型:
const char* cp; //錯誤:static_cast不能轉換const的性質 char *q=static_cast<char*>(cp); static_cast<string>(cp);//正確:字符串字面值轉換為string類型 const_cast<string>(cp);//const_cast只改變常量屬性
三、reinterpret_cast
從語法上看,這個操作符僅用於指針類型的轉換(返回值是指針)。它用來將一個類型指針轉換為另一個類型指針,它只需在編譯時重新解釋指針的類型。這個操作符基本不考慮轉換類型之間是否是相關的。(參見:紅心地瓜的博客和野男孩的博客)。
int *ip=NULL; char *pc=reinterpret_cast<char *>(ip); /*注:必須牢記pc所指的真實對象是一個int而非字符,如果把pc當成普通的字符指針使用 *就可能在運行時發生錯誤*/
在《C++ Primer(中文 第五版 )》指出reinterpret_cast很危險,不建議使用。
四、dynamic_cast
該運算符把expression轉換成type類型的對象。type必須是類型的指針、類的引用或者void*。type和expression的形式要對應,什么意思了?如:type是指針類型,那么expression也必須是一個指針。
與其他強制類型轉換不同,dynamic_cast設計運行時類型檢查。dynamic_cast運行時類型檢查需要運行時類型信息,而這個信息存儲在類的虛函數表中,只有定義了虛函數的類才有虛函數表,故對沒有虛函數表的類使用會導致dynamic_cast編譯錯誤。
另外,若綁定到引用或指針的對象類型不是目標類型,則dynamic_cast會失敗(這點下面細說)。若轉換到指針的失敗,dynamic_cast的結果是0值,若轉換到引用類型的失敗,則拋出一個bad_cast類型的異常。
dynamic_cast主要符主要用於類層次間的上行轉換和下行轉換。
1、在類層次間上行轉換時,dynamic_cast和static_cast的效果一樣。因為在公有繼承方式(保護繼承、私有繼承,不能隱式轉換)下,派生類的對象/對象指針/對象引用可以賦值給基類的對象/對象指針/對象引用(發生隱式轉換),反過來則不行。
2、若發生下行轉換是安全的,也就是,如果基類指針或者引用的確指向一個派生類對象,這個運算符會傳回轉型過的指針,若不安全,則會傳回空指針。
針對下行轉換,換句話說:向下轉換的成功與否還與將要轉換的類型有關,即要轉換的指針指向的對象的實際類型與轉換以后的對象類型一定要相同,否則轉換失敗。
class Base
{
public:
Base():b(1) {}
virtual void foo() {}
int b;
};
class Derived:public Base
{
public:
Derived():d(2) {}
int d;
};
void func(Base *p)
{
Derived *pd1=static_cast<Derived *>(p); //語句1
cout<<pd1->b<<endl;
cout<<pd1->d<<endl;
Derived *pd2=dynamic_cast<Derived *>(p); //語句2
cout<<pd2->b<<endl;
cout<<pd2->d<<endl;
}
1)若調用函數func的實參p指向一個Derived類型的對象,即
Base *p=new Derived; func(p);
則pd1和pd2是一樣的,並且對這兩個指針執行 Derived類的任何操作都是安全的,語句1和2都是輸出1、2;
2)若p指向的是一個Base類型的對象,即
Base *p=new Base; func(p);
那么pd1指向Base對象的地址,對它進行Derived類型的操作將是不安全的(如訪問d),輸出d的值時,將會是一個垃圾值;而pd2將是一個空指針,對空指針進行操作,將會發生異常。
參考資料:《C++ primer》(第五版)
