c++ - reinterpret_cast - 經常轉換與static_cast vs. dynamic_cast




static_cast c++用法 (6)

我一直在編寫C和C ++代碼近二十年,但這些語言的一個方面我從來沒有真正理解。 我顯然使用了常規演員,即

MyClass *m = (MyClass *)ptr;

到處都是,但似乎還有其他兩種類型的演員,我不知道區別。 以下幾行代碼有什麼區別?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);

的static_cast

static_cast用於基本上想要反轉隱式轉換的情況,只有一些限制和附加。 static_cast執行運行時檢查。 如果你知道你指的是一個特定類型的對象,那麼應該使用它,因此檢查是不必要的。 例:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

在這個例子中,你知道你傳遞了一個MyClass對象,因此不需要運行時檢查來確保這一點。

的dynamic_cast

當您不知道對象的動態類型是什麼時, dynamic_cast非常有用。 如果引用的對像不包含作為基類轉換的類型(當您轉換為引用時,在此情況下引發bad_cast異常),它將返回空指針。

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

如果向下轉換(轉換為派生類)並且參數類型不是多態,則不能使用dynamic_cast 。 例如,以下代碼無效,因為Base不包含任何虛函數:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

“向上投射”( static_cast轉換為基類)對於static_castdynamic_cast都是有效的,並且也沒有任何投射,因為“上投射”是隱式轉換。

定期演員

這些演員也被稱為C型演員。 C風格的轉換基本上與嘗試一系列C ++轉換序列相同,並且可以在不考慮dynamic_cast情況下採用第一個可以工作的C ++轉換。 不用說,這更強大,因為它結合了所有的const_caststatic_castreinterpret_cast ,但它也是不安全的,因為它不使用dynamic_cast

另外,C風格轉換不僅可以讓你做到這一點,而且它們還允許你安全地轉換到私有基類,而“等效” static_cast序列會給你一個編譯時錯誤。

有些人更喜歡C風格的演員,因為他們簡潔。 我僅將它們用於數字轉換,並且在涉及用戶定義的類型時使用適當的C ++類型轉換,因為它們提供更嚴格的檢查。


靜態投射

靜態轉換執行兼容類型之間的轉換。 它類似於C風格的演員,但更具限制性。 例如,C風格的轉換將允許一個整數指針指向一個字符。

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

由於這會產生一個指向分配內存1字節的4字節指針,寫入該指針將導致運行時錯誤或覆蓋某些相鄰內存。

*p = 5; // run-time error: stack corruption

與C風格轉換相反,靜態轉換將允許編譯器檢查指針和指針數據類型是否兼容,這允許程序員在編譯期間捕獲這個不正確的指針分配。

int *q = static_cast<int*>(&c); // compile-time error

重新演繹演員

要強制轉換指針,與C風格轉換在後台執行相同的操作,將使用reinterpret強制轉換。

int *r = reinterpret_cast<int*>(&c); // forced conversion

此演員處理某些不相關類型之間的轉換,例如從一個指針類型到另一個不兼容的指針類型。 它將簡單地執行數據的二進制拷貝而不改變基礎位模式。 請注意,這種低級別操作的結果是系統特定的,因此不可移植。 如果無法完全避免,應謹慎使用。

動態投射

這個僅用於將對象指針和對象引用轉換為繼承層次結構中的其他指針或引用類型。 它是通過執行運行時檢查指針指向目標類型的完整對象來確保指向的對象可以轉換的唯一類型。 對於此運行時檢查,對象必須是多態的。 也就是說,該類必須定義或繼承至少一個虛擬功能。 這是因為編譯器只會為這些對像生成所需的運行時類型信息。

動態演員示例

在下面的示例中,使用動態強制轉換將MyChild指針轉換為MyBase指針。 此派生到基礎的轉換成功,因為子對象包含完整的基礎對象。

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

下一個示例嘗試將MyBase指針轉換為MyChild指針。 由於Base對像不包含完整的Child對象,因此此指針轉換將失敗。 為了表明這一點,動態強制轉換返回一個空指針。 這提供了一種便捷的方法來檢查運行時轉換是否成功。

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

如果引用被轉換而不是指針,則動態轉換將通過拋出bad_cast異常而失敗。 這需要使用try-catch語句來處理。

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

動態或靜態演員

使用動態轉換的優點在於,它允許程序員在運行時檢查轉換是否成功。 缺點是與執行此檢查相關的性能開銷。 出於這個原因,在第一個示例中使用靜態轉換會更可取,因為派生到基準轉換永遠不會失敗。

MyBase *base = static_cast<MyBase*>(child); // ok

但是,在第二個示例中,轉換可能成功或失敗。 如果MyBase對象包含MyBase實例,它將失敗,如果它包含MyChild實例,它將成功。 在某些情況下,這可能直到運行時才會被發現。 在這種情況下,動態投射比靜態投射更好。

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

如果使用靜態轉換而不是動態轉換來執行基於派生的轉換,則轉換不會失敗。 它會返回一個指向不完整對象的指針。 解引用這樣的指針會導致運行時錯誤。

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

Const施放

這個主要用於添加或刪除變量的const修飾符。

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

雖然const轉換允許改變常量的值,但這樣做仍然是無效的代碼,可能會導致運行時錯誤。 例如,如果常量位於只讀存儲器的一部分中,則可能發生這種情況。

*nonConst = 10; // potential run-time error

當一個函數接受一個非常量的指針參數時,即使它不修改指針,主要是使用const強制轉換。

void print(int *p) 
{
   std::cout << *p;
}

該函數可以通過使用const轉換來傳遞一個常量變量。

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

來源和更多的解釋


dynamic_cast具有運行時類型檢查功能,僅適用於引用和指針,而static_cast不提供運行時類型檢查。 有關完整信息,請參閱MSDN文章static_cast運算符


dynamic_cast只支持指針和引用類型。 如果類型是一個指針時不可能轉換,則返回NULL如果類型是引用類型,則返回異常。 因此, dynamic_cast可以用來檢查一個對像是否是給定的類型, static_cast不能(你最終會得到一個無效的值)。

其他答案已經涵蓋了C式(和其他)演員。


僅供參考,我相信Bjarne Stroustrup被引述說,應避免使用C風格的演員陣容,如果可能的話應該使用static_cast或dynamic_cast。

Barne Stroustrup的C ++風格常見問題

就你的意願採取這個建議。 我遠非C ++大師。


避免使用C風格演員。

C風格演員陣容是const和reinterpret演員陣容的混合體,很難在代碼中找到並替換。 C ++應用程序員應該避免C風格的轉換。







casting