類型轉換

在C++中,兩個類型相關的定義是:這兩個類型之間可以相互轉。

一、隱式轉換

C++不把兩個不同類型的值加在一起,而是提供了一組轉換規則,以便在執行算數操作前將兩個操作數轉換爲同一種類型。這類不需要程序員介入而直接執行的轉換稱爲隱式類型轉換。

C++定義的算術類型之間的內置轉換儘可能防止精度損失。編譯器在必要時將類型轉換規則應用到內置類型和類類型的對象。

1、發生隱式類型轉換的情況:

(1)、在混合類型表達式中,其操作數被轉換爲相同的類型:

  1. int ival;

  2. double dval;

  3. ival >= dval;//ival轉換爲double

(2)、用作條件表達式被轉換爲bool類型:

  1. int ibal;

  2. if (ival)      //ival被抓環衛bool型

  3. while (cin)    //ival被抓環衛bool型

條件操作符(?:)中的第一個操作數以及邏輯非(!)、邏輯與(&&)和邏輯或(||)的操作數都是條件表達式。出現在if、while、for和do...while語句中的同樣也是條件表達式。

(3)、用一表達式初始化某個變量,或將一表達式賦值給某個變量,則該表達式被轉換爲該變量的類型:

  1. int ival = 3.14;   //3.14轉換爲int型

  2. int *ip;

  3. ip = 0;          //0被轉換爲int型空指針

2、算數轉換:

此類隱式轉換最常用,它保證在執行操作之前,將二元操作符的兩個操作數轉換爲同一類型,並使表達式的值也具有相同的類型。最簡單的轉換爲整型提升:對所有比int小的整型,包括bool(0或1)、char、signed char、unsigned char、short和unsigned short,如果該類型的所有可能的值都能包容在int內,它們就會被提升爲int型,否則,它們將被提升爲unsigned int。

有符號與無符號類型之間的轉換:

這類轉換本質上依賴機器。因爲如果表達式使用了無符號數值,那麼所定義的轉換規則需保護操作數的精度。而unsigned操作數的轉換依賴於機器中整型的相對大小。

對unsigned short/int和int/long的轉換,如果int/long型足夠表示unsigned short/int型的值,九江unsigned short/int轉換爲int/long型,否則將兩個操作數轉換爲unsigned int/long。

3、指針轉換:

使用數組時,大多數情況下數組都會自動轉換爲指向第一個元素的指針:

  1. int ia[10];

  2. int *ip = ia;  //ia轉換爲指向數組中第一個元素的指針

例外情況有:

數組用作取地址(&)操作符的操作數或sizeof操作符的操作數時,或用數組對數組的引用進行初始化時。

PS:指向任意數據類型的指針都可以轉換爲void*類型;整型數值常量0可轉換爲任意指針類型。

4、轉換爲bool類型:

算數值和指針值都可以轉換爲bool類型。對應false和true。

bool型也可以轉換爲int型,對應爲0和1。

5、轉換與枚舉類型:

C++自動將枚舉類型的對象或枚舉成員轉換爲整型,該轉換結果可用於任何要求使用整數值的地方。而將enum對象或枚舉成員提升爲什麼類型由機器定義,並且依賴於枚舉成員的最大值。

6、轉換爲const對象:

當使用非const對象初始化const對象的引用時,系統將非const對象轉換爲const對象。此外,還可以將非const對象的地址(或非const指針)轉換爲指向相關const類型的指針:

  1. int i;

  2. constint ci = 0;

  3. constint &j = i;

  4. constint *p = &ci;

  5. constint *p = &i;

二、顯式轉換

即強制類型轉換。

命名的強制類型轉換符號的一般形式如下:

  1. cast-name<type>(expression);

其中cast-name爲static_cast、dynamic_cast、const_cast和reinterpret_cast之一,type爲轉換的目標類型,而expression則是被強制轉換的值。

1、dynamic_cast

dynamic_cast支持運行時識別指針或引用所指向的對象。與其一起使用的指針必須是有效的。

與其它三者不同的是,它涉及運行時類型檢查。如果轉換到指針類型的dynamic_cast失敗,則dynamic_cast的結果是0值;若轉換到引用類型的dynamic_cast失敗,則拋出一個bad_cast類型的異常。

因此,該操作符一次執行兩個操作。它首先驗證被請求的轉換是否有效,只有轉換有效,操作符才實際進行轉換。

2、const_cast

該操作符可以轉換掉表達式的const性質:

  1. const char *pc_ptr;

  2. char *pc = string_copy(const_cast<char*>(pc_str));

只有使用const_cast才能將const性質轉換掉,而且,除了添加與刪除const特性,用const_cast符來執行其它任何類型轉換,都會引起編譯錯誤。

3、static_cast

編譯器隱式執行的任何類型轉換都可以用static_cast顯示完成:

  1. double d = 97.0;

  2. char ch = static_cast<char>(d);

當需要將一個較大的算術類型賦值給較小的類型時,使用強制轉換非常有用。

4、reinterpret_cast

reinterpret_cast通常爲操作數的位模式提供較低層次的重新解釋。該操作符本質上依賴於機器,因此其安全性應重點考慮。

  1. int *ip;

  2. char *pc = reinterpret_cast<char*>(ip);

程序員應該記得pc所指向的真實對象其實是int型,而並非字符數組。任何假設pc是普通字符指針的應用,都有可能帶來未知的運行時錯誤。

PS:應儘量避免使用強制類型轉換,不依賴強制類型轉換往往也能寫出很好的C++程序。

三、實參類型轉換(函數重載)

類型提升或轉換適用於實參類型可通過某種標準轉換提升或轉換爲適當的形參類型的情況。

必須注意的一個重點是較小的整型提升爲int型。假設有兩個函數,一個的形參爲int型,另一個的形參則是short型。對於任意整型的實參值,int版本都是由於short版本的較佳匹配,即使從形式上看short型版本的匹配更佳:

  1. void ff(int);

  2. void ff(short);

  3. ff('a');        //char提升爲int

四、轉換與類類型

我們可以用一個實參調用的非explicit構造函數定義一個隱式轉換,當提供了實參類型的對象而需要一個類類型的對象時,編譯器將使用該轉換。

除了定義類類型的轉換外,我們還可以定義從類類型的轉換。即,定義轉換操作符,給定類類型的對象,該操作符將產生其他類型的對象,而且編譯器也將自動應用這個轉換。其用途如下:

1、支持混合類型表達式;

2、轉換減少所需操作符的數目。

轉換操作符有:

轉換操作符是一種特殊的類成員函數,它定義將類類型值轉變爲其他類型值的轉換。轉換操作符在類定義體內聲明,在保留字operator之後跟着轉換的目標類型:

  1. operator type();

type表示內置類型名、類類型名由類型別名所定義的名字。對任何可作爲函數返回類型的類型(除void外)都可以定義轉換函數。一般而言,不允許轉換爲數組或函數類型,轉換爲指針類型(數據和函數指針)以及引用類型是可以的。

雖然轉換函數不能指定返回類型,但是每個轉換函數必須顯式返回一個指定類型的值。

PS:轉換函數必須是成員函數,不能指定返回類型,並且形參表必須爲空。轉換函數一般不應該改變被轉換的對象。因此,轉換操作符通常應定義爲const成員。

1、使用類類型轉換

只要存在轉換,編譯器將在可以使用內置轉換的地方自動調用它:表達式中、條件中、實參傳值、重載、顯示類型中等。

2、類類型轉換和標準轉換

使用轉換函數時,被轉換的類型不必與所需要的類型完全匹配。必要時可以在類類型轉換之後跟上標準轉換以獲得想要的類型。

3、只能應用一個類類型轉換

類類型轉換之後不能再跟另一個列類型轉換。如果需要多個類類型轉換,則代碼將出錯。

4、標準轉換可放在類類型轉換之前

使用構造函數執行隱式轉換時,構造函數的形參類型不必與所提供的類型完全匹配。如果需要,在調用構造函數執行類類型轉換之前,可將一個標準轉換序列應用於實參。

五、轉換與繼承

主要是基類類型和派生類型之間的轉換。

1、派生類到基類的轉換

如果有一個派生類型的對象,則可以使用它的地址對基類類型的指針進行賦值或初始化。同樣,可以使用派生類型的引用或對象初始化基類類型的引用。

(1).引用轉換不同於轉換對象

我們可以將派生類型的對象傳給希望接受基類引用的函數,這時,引用直接綁定到該對象,實參只是該對象的引用,對象本身未被複制。

(2).用派生類對象對基類對象進行初始化或賦值

對基類對象進行初始化或賦值實際上實在調用函數:初始化時調用構造函數,賦值時調用賦值操作符。正因爲存在從派生類引用到基類引用的轉換,才使賦值控制成員可用於從派生類對象對基類對象進行初始化或賦值成爲可能。

(3).派生類到基類轉換的可訪問性

像繼承的成員函數一樣,從派生類到基類的轉換可能是也可能不是可訪問的,這取決於在派生類的派生列表中指定的訪問標號。

PS:要確定到基類的轉換是否可訪問,可以考慮基類的public成員是否可訪問,如果可以,則轉換是可訪問的,否則,轉換是不可訪問的。

2、基類到派生類的轉換

從基類到派生類的自動轉換是不存在的,需要派生類對象時不能使用基類對象,因爲基類對象只能是基類對象對象,不能包含派生類型的成員。

當基類指針或引用實際綁定到派生類對象時,從基類到派生類的轉換也是存在限制的,編譯器確定轉換是否合法,只看指針或引用的靜態類型。在此情況下,如果知道從基類到派生類的轉換是安全的,就可以使用static_cast強制編譯器進行轉換,或者可以用dynamic_cast申請在運行時進行檢查。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章