天天看點

一文總結C++的dynamic_cast 和 static_cast

作者:嵌入式小生

一、開篇

dynamic_cast和static_cast屬于類型轉換的範疇,準确的是顯式類型轉換,本文主要總結C++中的顯式類型轉換。

一個命名的強制類型轉換有如下的格式:

cast_name<type>(expression)           

上述格式中:

  • type:是轉換的目标類型。
  • expression:是要轉換的值。
  • cast_name:是類型轉換的方式。有四種:static_cat、dynamic_cast、const_cast、reinterpret_cast。

二、static_cast類型轉換

static_cast具有很廣的使用場景:在類型轉換中,隻要不包含底層const,任何具有明确定義的類型轉換,都可以使用static_cast。使用範圍雖廣,但是還需要根據具體的轉換風險來選擇,因為強制類型轉換,本質上是一個危險的操作。

【低風險轉換】

空指針轉換為任何目标類型的指針之間的轉換

字元與整型之間的轉換

整型和浮點型之間的轉換

運算符之間的轉換

低于低風險轉換,則可以使用static_cast進行。

【高風險轉換】

不同類型指針之間的轉換

不同類型的引用之間的轉換

整型和指針之間的互相轉換

對于以上風險較高的轉換,則不能使用static_cast。

【基類與派生類之間的轉換】

子類轉換成父類,可以使用static_cast。

父類轉換成子類,是一個不安全的操作,是以不能使用static_cast。這時候需要使用後文中的dynamic_cast類型轉換來進行轉換啦!

三、const_cast類型轉換

對于const_cast類型轉換來說,需要注意幾點: (1)const_cast隻能改變運算對象的底層const。

(2)隻有const_cast類型轉換能改變表達式的常量屬性,其他的類型轉換不能,如果使用,則會引發編譯時錯誤!

(3)不能使用const_cast改變表達式的類型。

(4)如果對象是一個常量,我們使用了const_cast執行對該對象的寫操作将會是一個未定義的後果!

以上四點,可以用以下代碼來逐一解釋:

const char *cp;

//隻有const_cast類型轉換能改變表達式的常量屬性,其他的類型轉換不能,如果使用,則會引發編譯時錯誤!
static_cast<char *>(cp);

//不能使用const_cast改變表達式的類型。
const_cast<string>(cp);           
const_cast常常用于有函數重載的上下文中。

例如下列代碼:

int getAge(Student *);
int getAge(const Student *)           

上述兩個函數是重載函數,在實際工程中,編譯器可以根據實參是否是常量來推斷應該調用哪個函數,因為const不能轉換成其他類型,是以隻能把const修飾的對象傳遞給帶有const形參。

四、reinterpret_cast類型轉換

reinterpret_cast為運算對象的位模式提供較低層次上的重新解釋。在《C++ Primer》一書中明确指出使用reinterpret_cast是非常危險的操作。

reinterpret_cast本質上依賴于具體的機器。要想安全的使用reinterpret_cast必須對涉及的類型和所使用的編譯器實作轉換的過程都非常了解。

綜上,在實際C++使用中,如果沒有足夠的把握,避免使用reinterpret_cast進行類型轉換。

五、dynamic_cast類型轉換

dynamic_cast是運作時類型識别(RTTI)。該運算符用于:将基類的指針或引用安全的轉換成派生類的指針或引用。該種類型轉換适用于:當我們想使用基類對象的指針或引用執行某個派生類操作并且該操作不是虛函數。(一般說來,我們應該盡可能的使用虛函數)

dynamic_cast運算符的使用形式有以下三種:

dynamic_cast<type*>(e)

dynamic_cast<type&>(e)

dynamic_cast<type&&>(e)           

type必須是一個類類型,并且通常情況下該類型應該有虛函數。在上述第一種形式中,e必須是一個有效的指針。第二種形式中,e必須是一個左值;在第三種形式中,e不能是左值。

在使用dynamic_cast中,應該對轉換後的結果進行檢查。避免轉換失敗!

轉換的對象有指針和引用:

(5-1)指針類型的dynamic_cast

對于指針類型的dynamic_cast,當轉換失敗,其轉換值為0,我們可以根據轉換後結果值是否為0判斷轉換是否成功!

dynamic_cast使用方法如下:

void funCast(BaseClass *bc)
{
  if(MyClass *mc = dynamic_cast<BaseClass*>(bc))
  {
    //轉換成功
    //...
  }
  else
  {
    //轉換失敗情況處理
    //...
  }

}           
可以對一個空指針執行dynamic_cast,其轉換結果為所需類型的空指針。

(5-2)引用類型的dynamic_cast

對于引用類型的dynamic_cast,當類型轉換失敗,程式會抛出一個std::bad_cast的異常,該異常定義在typeinfo标準庫檔案中。我們可以使用類似下列的代碼來捕獲這個異常:

void funCast(const BaseClass &bc)
{
  try
  {
    const MyClass &mc = dynamic_cast(BaseClass&)(bc);
    
    //後續操作
  }
  catch(std::bad_cast)
  {
    //處理類型轉換失敗的情況
  }

}           

六、總結

類型檢查,在實際開發中是一種有效規避錯誤的方式,主要由編譯建構完成,程式越複雜,靜态類型檢查越能發揮巨大作用。然而強制類型轉換幹擾了正常的類型檢查,是以在實際的開發中,盡量避免使用強制類型轉換,規則如下:

(1)對于reinterpret_cast:不要使用。

(2)對于const_cast:在有重載函數的上下文中使用const_cast無可厚非。在其他情況下使用const_cast則證明該處程式存在設計缺陷。

(3)對于static_cast和dynamic_cast:不應該頻繁使用。

【注】在代碼中,每次書寫了強制類型轉換,都應該仔細斟酌是否有替代方式實作相同的目标。如果無法避免,應該盡量限制強制類型轉換值的作用域。