天天看點

C++ 進階篇(四)—— 類型轉換進階

目前為止,我們一直使用傳統的類型轉換符來進行簡單對象的類型轉換。例如,要把一個double類型的浮點型數字轉換為int 的整型數字,我們是這樣做的:

int i; double d; i = (int) d;

或者

i = int (d);

這樣做對基本資料類型時沒問題的,因為基本資料類型的轉換已經有标準的定義。同樣的操作也可以被在類或類的指針上,是以以下例子中的寫法也是沒有問題的:

// class type-casting
    #include <iostream.h>
    
    class CDummy {
        int i;
    };
    
    class CAddition {
        int x,y;
      public:
        CAddition (int a, int b) { x=a; y=b; }
        int result() { return x+y;}
    };
    
    int main () {
        CDummy d;
        CAddition * padd;
        padd = (CAddition*) &d;
        cout << padd->result();
        return 0;
    }
		      

雖然以上程式在C++中是沒有文法錯誤的(多數編譯器甚至不會産生警告資訊),但這段程式沒有什麼實際的邏輯意義。我們使用了CAddition 的成員函數result 而沒有定義一個相應的該類的對象:padd 并不是一個對象,它隻是一個指針,被我們指派指向一個毫無關系的對象的位址。當在程式運作到通路它的result 成員函數時,将會有一個運作錯誤(run-time error)産生,或生成一個意外的結果。

為了控制這種類之間的轉換,ANSI-C++ 标準定義了4種新的類型轉換操作符: reinterpret_cast, static_cast, dynamic_cast 和 const_cast。所有這些操作符都是同樣的使用格式:

reinterpret_cast <new_type> (expression)

dynamic_cast <new_type> (expression)

static_cast <new_type> (expression)

const_cast <new_type> (expression)

這裡new_type 是要轉換成的目标類型,expression 是要被轉換的内容。為了便于了解,模仿傳統轉換操作符,它們的含義是這樣的:

(new_type) expression new_type (expression) 

reinterpret_cast

reinterpret_cast 可以将一個指針轉換為任意其它類型的指針。它也可以用來将一個指針轉換為一個整型,或反之亦然。

這個操作符可以在互不相關的類之間進行指針轉換,操作的結果是簡單的将一個指針的二進制資料(binary copy)複制到另一個指針。對指針指向的内容不做任何檢查或轉換。

如果這種複制發生在一個指針到一個整數之間,則對其内容的解釋取決于不同的系統,是以任何實作都是不可移植(non portable)的。一個指針如果被轉換為一個能夠完全存儲它的足夠大的整數中,則是可以再被轉換回來成為指針的。

例如:

class A {};

class B {};

A * a = new A;

B * b = reinterpret_cast<B*>(a);

reinterpret_cast 對所有指針的處理與傳統的類型轉換符所作的一模一樣。

static_cast

static_cast 可以執行所有能夠隐含執行的類型轉換,以及它們的反向操作(即使這種方向操作是不允許隐含執行的)。

用于類的指針,也就是說,它允許将一個引申類的指針轉換為其基類類型(這是可以被隐含執行的有效轉換),同時也允許進行相反的轉換:将一個基類轉換為一個引申類類型。

在後面一種情況中,不會檢查被轉換的基類是否真正完全是目标類型的。例如下面的代碼是合法的:

class Base {};

class Derived: public Base {};

Base * a = new Base;

Derived * b = static_cast(a);

static_cast除了能夠對類指針進行操作,還可以被用來進行類中明确定義的轉換,以及對基本類型的标準轉換:

double d=3.14159265; int i = static_cast<int>(d);

譯者注:如果你對這部分看不太懂,請結合下面的dynamic_cast一起看,也許會幫助了解。

dynamic_cast

dynamic_cast 完全被用來進行指針的操作。它可以用來進行任何可以隐含進行的轉換操作以及它們被用于多态類情況下的方向操作。然而與static_cast不同的是, dynamic_cast 會檢查後一種情況的操作是否合法,也就是說它會檢查類型轉換操作是否會傳回一個被要求類型的有效的完整的對象。

這種檢查是在程式運作過程中進行的。如果被轉換的指針所指向的對象不是一個被要求類型的有效完整的對象,傳回值将會是一個空指針NULL 。

class Base { virtual dummy(){}; };
   class Derived : public Base { };
   

   Base* b1 = new Derived;
   Base* b2 = new Base;
   Derived* d1 = dynamic_cast(b1);   // succeeds
   Derived* d2 = dynamic_cast(b2);   // fails: returns NULL
         

如果類型轉換被用在引用(reference)類型上,而這個轉換不可能進行的話,一個bad_cast 類型的例外(exception)将會被抛出:

class Base { virtual dummy(){}; };
  class Derived : public Base { };
  
  Base* b1 = new Derived;
  Base* b2 = new Base;
  Derived d1 = dynamic_cast(b1);   // succeeds
  Derived d2 = dynamic_cast(b2);   // fails: exception thrown
        

const_cast

這種類型轉換對常量const 進行設定或取消操作:

class C {};

const C * a = new C;

C * b = const_cast<C*> (a);

其他3種cast 操作符都不可以修改一個對象的常量屬性(constness)。

typeid

ANSI-C++ 還定義了一個新的操作符叫做 typeid ,它檢查一個表達式的類型:

typeid (expression)

這個操作符傳回一個類型為type_info的常量對象指針,這種類型定義在标準頭函數中。這種傳回值可以用操作符 == 和 != 來互相進行比較,也可以用來通過name()函數獲得一個描述資料類型或類名稱的字元串,例如:

// typeid, typeinfo
    #include <iostream.h>
    #include <typeinfo>
    
    class CDummy { };
    
    int main () {
        CDummy* a,b;
        if (typeid(a) != typeid(b)) {
            cout << "a and b are of different types:\n";
            cout << "a is: " << typeid(a).name() << '\n';
            cout << "b is: " << typeid(b).name() << '\n';
        }
        return 0;
    }
			      

a and b are of different types:

a is: class CDummy *

b is: class CDummy