static_cast 和 reinterpret_cast

來源:互聯網
上載者:User

<<static_cast 和 reinterpret_cast>>

作者: 闕榮文(querw@sina.com)

C/C++是強型別語言,不同類型之間的相互轉換是比較麻煩的.但是在編程實踐中,不可避免的要用到類型轉換.有2中類型轉換:隱式類型轉換和強制類型轉換.

1.隱式類型轉換
1.1 提升精度,此種是編譯器自動完成的,安全的.所以編譯的時候不會有任何錯誤或者警告資訊提示.
樣本: <<C++ Primer (第三版)>> P147
int ival = 3;
double dval = 3.14159;

// ival 被提升為 double 類型: 3.0
ival + dval;

1.2 降低精度,也是有編譯器自動完成,會造成精度丟失,所以編譯時間得到一個警告資訊提示.
樣本:
double dval = 3.14159;
// dval的值被截取為 int 值3
int ival = dval;

2.顯式類型轉換

2.1 C風格的強制轉換(包括舊式C++風格的強制轉換)

格式:
類型(運算式); // 舊的C++風格
或者
(類型)運算式 // C風格

樣本: int(dval) 或者 (int)dval

此種強制轉換是比較粗暴直接的,有可能導致精度丟失(如從 double 轉換為 int)或者一些莫名其妙的錯誤(如把 int 轉換為 函數指標),一旦使用了強制轉換,編譯器將不提示任何警告.這也往往成為錯誤的源泉.而且這種錯誤非常難找.我想這也是C++要使用新的強制轉換操作符的原因之一吧.

2.2 C++強制轉換操作符
C++增加了4個關鍵字用於強制類型轉換:
static_cast, reinterpret_cast, const_cast 和 dynamic_cast.

const_cast 用來移除 const,這個沒什麼好說的.
dynamic_cast 需要 RTTI 支援, 主要用於把基類指標轉換為衍生類別指標.這裡的基類指標其實是指向一個衍生類別執行個體,只是類型為基類.
樣本:
// 前提假設: class B 由 class A 派生
A *ptrA = new class B;
B *ptrB = dynamic_cast<B*>(ptrA);

本文主要談談 static_cast 和 reinterpret_cast 的用法和區別.
<<C++程式程式設計語言>>裡有一句話我認為說到點子上了: static_cast 運算子完成*相互關聯類型*之間的轉換. 而 reinterpret_cast 處理*互不相關的類型*之間的轉換.

所謂"相互關聯類型"指的是從邏輯上來說,多多少少還有那麼一點聯絡的類型,比如從 double 到 int,我們知道它們之間還是有聯絡的,只是精度差異而已,使用 static_cast 就是告訴編譯器:我知道會引起精度損失,但是我不在乎. 又如從 void* 到 具體類型指標像 char*,從語義上我們知道 void* 可以是任意類型的指標,當然也有可能是 char* 型的指標,這就是所謂的"多多少少還有那麼一點聯絡"的意思. 又如從衍生類別層次中的上行轉換(即從衍生類別指標到基類指標,因為是安全的,所以可以用隱式類型轉換)或者下行轉換(不安全,應該用
dynamic_cast 代替).
對於static_cast操作符,如果需要截斷,補齊或者指標位移編譯器都會自動完成.注意這一點,是和 reinterpret_cast 的一個根本區別.

"互不相關的類型"指的是兩種完全不同的類型,如從整型到指標類型,或者從一個指標到另一個毫不相干的指標.
樣本:
int ival = 1;
double *dptr = reinterpret_cast<double*>(ival);

或者
int *iptr = NULL;
double *dptr = reinterpret_cast<double*>(iptr);

reinterpret_cast 操作執行的是位元位拷貝,就好像用 memcpy() 一樣.

int *iptr = reinterpret_cast<int*>(1);
double *dptr = reinterpret_cast<double*>(2);
memcpy(&dptr, &iptr, sizeof(double*)); // 等效於 dptr = reinterpret_cast<double*>(iptr); 結果 dptr 的值為1;

上面這個樣本也說明了 reinterpret_cast 的意思:編譯器不會做任何檢查,截斷,補齊的操作,只是把位元位拷貝過去.
所以 reinterpret_cast 常常被用作不同類型指標間的相互轉換,因為所有類型的指標的長度都是一致的(32位系統上都是4位元組),按位元位拷貝後不會損失資料.

3. 編程實踐中幾種典型的應用情境

3.1 數值精度提示或者降低,包括把無符號型轉換為帶符號型(也是精度損失的一種),用 static_cast 可以消除編譯器的警告資訊,前面提到好幾次了.

3.2 任意類型指標到 void*, 隱式類型轉換,自動完成. 看看 memcpy 的原型
void *memcpy(
   void *dest,
   const void *src,
   size_t count
);
參數定義為 void* 是有道理的,不管我們傳入什麼類型的指標都符合語義,並且不會有編譯器警告.

3.3 void* 到任意類型指標, 用 static_cast 和 reinterpret_cast 都可以,這是由 void* 是通用指標這個語義決定的.我個人傾向用 reinterpret_cast,表達要"重新解釋"指標的語義.

3.4 不同類型指標間的相互轉換用 reinterpret_cast.

3.5 int 型和指標類型間的相互轉換用 reinterpret_cast.
比如我寫代碼的時候經常這樣做: new 一個 struct,然後把指標返回給外部函數作為一個"控制代碼",我不希望外部函數知道這是一個指標,只需要外部函數在調用相關函數時把這個"控制代碼"重新傳回來.這時,就可以把指標轉換為一個 int 型返回. 這是 reinterpret_cast 存在的絕佳理由.

struct car
{
    int doors;
    int height;
    int length;
    float weight;
};

int create_car()
{
    car *c = new car;
    return reinterpret_cast<int>(c);
}

int get_car_doors(int car_id)
{
    car *c = reinterpret_cast<car*>(car_id);
    return c->doors;
}

void destroy_car(int car_id)
{
    car *c = reinterpret_cast<car*>(car_id);
    delete c;
}

如上,外部函數不需要知道 struct car 的具體定義,只需要調用 create_car() 得到一個 car id,然後用此 car_id 調用其他相關函數即可,至於 car_id 是什麼,根本沒必要關心.

3.6 衍生類別指標和基類指標間的相互轉換.
3.6.1 衍生類別指標到基類指標用隱式類型轉換(直接賦值)或者用 static_cast. 顯然不應該也沒必要用 reinterpret_cast.
3.6.2 基類指標到衍生類別指標用 dynamic_cast (運行期檢查)或者 static_cast (運行期不檢查,由程式員保證正確性). 考慮到C++物件模型的記憶體分布可能引起的指標位移問題,絕對不能用 reinterpret_cast.

後記
幾乎所有提到 reinterpret_cast 的書籍都要附帶說什麼"不可移植","危險"之類的詞,好像 reinterpret_cast 是洪水猛獸,碰不得摸不得.其實理解了之後就知道沒什麼神秘的,存在即是理由,該用的時候就要大膽的用,否則C++保留這個關鍵字幹什麼? 關鍵是程式員應該清楚的知道自己要的結果是什麼,如此,就是用C風格的強制轉換又有何妨?

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.