C++中的顯示類型轉換

C++中顯示轉換也成爲強制類型轉換(cast),有四種:static_cast、dynamic_cast、const_cast、reinterpret_cast。命名的強制類型轉換符號通常形式以下:express

cast_name<type>(expression);

如下分別介紹安全

1、static_cast函數

任何具備明肯定義的類型轉換,只要不包含底層const均可以使用static_cast。好吧這句話我不是很懂,換句話:編譯器隱式執行的任何類型轉換均可以由static_cast顯示完成。也就是說,兩類型之間能夠發生隱式的轉換,就能夠用static_cast顯示轉換,有點意思。但要知道的是C++基本類型的指針之間不含有隱式轉換(void*除外、const的有些也是能夠的),須要顯示轉換。什麼意思?以下:spa

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的轉換纔是合法的,不然將出錯。(基類指針或引用轉換成子類指針或引用爲下行轉換).net

類層次間的下行轉換不能經過隱式轉換完成,可是能夠經過static_cast完成,可是因爲沒有動態類型檢查,因此是不安全的。(至於這個不安全,咱們會在dynamic_cast中具體說)。設計

class Base{};
class child:public Base{};
 
Base b;
child c;
c=static_cast<child *>(b);  //下行轉換,正確;
c=b;    //編譯錯誤

 2、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的:blog

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只改變常量屬性

3、reinterpret_cast

從語法上看,這個操做符僅用於指針類型的轉換(返回值是指針)。它用來將一個類型指針轉換爲另外一個類型指針,它只需在編譯時從新解釋指針的類型。這個操做符基本不考慮轉換類型之間是不是相關的。(參見:紅心地瓜的博客野男孩的博客)。

int *ip=NULL;
char *pc=reinterpret_cast<char *>(ip);
 
/*注:必須牢記pc所指的真實對象是一個int而非字符,若是把pc當成普通的字符指針使用
*就可能在運行時發生錯誤*/

 在《C++ Primer(中文 第五版 )》指出reinterpret_cast很危險,不建議使用。

4、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主要符主要用於類層次間的上行轉換和下行轉換。

一、在類層次間上行轉換時,dynamic_cast和static_cast的效果同樣。由於在公有繼承方式(保護繼承、私有繼承,不能隱式轉換)下,派生類的對象/對象指針/對象引用能夠賦值給基類的對象/對象指針/對象引用(發生隱式轉換),反過來則不行。

二、若發生下行轉換是安全的,也就是,若是基類指針或者引用的確指向一個派生類對象,這個運算符會傳回轉型過的指針,若不安全,則會傳回空指針。

針對下行轉換,換句話說:向下轉換的成功與否還與將要轉換的類型有關,即要轉換的指針指向的對象的實際類型與轉換之後的對象類型必定要相同,不然轉換失敗。

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都是輸出一、2;

2)若p指向的是一個Base類型的對象,即

Base *p=new Base;
func(p);

 那麼pd1指向Base對象的地址,對它進行Derived類型的操做將是不安全的(如訪問d),輸出d的值時,將會是一個垃圾值;而pd2將是一個空指針,對空指針進行操做,將會發生異常。

參考資料:《C++ primer》(第五版)

相關文章
相關標籤/搜索