C++語言基礎(1):類型轉(zhuǎn)換

相關(guān)章節(jié)

「C++類的特殊成員函數(shù)(1):構(gòu)造函數(shù)」中“3 隱式類類型轉(zhuǎn)換”


考慮下面的例子:

int ival = 0;
ival = 3.541 + 3;   //編譯器會給出警告

在計算ival的值時,C++不會把兩個不同類型的值直接相加,而是提供了一套轉(zhuǎn)換規(guī)則,在做加法計算前,先將兩個操作數(shù)轉(zhuǎn)換為同一種數(shù)據(jù)類型

這里ival最終的值為6。在賦值操作中,不可能更改左操作數(shù)對象的類型。如果賦值操作符的左右操作數(shù)類型不同,那么右操作數(shù)會被轉(zhuǎn)換為左操作數(shù)的類型。

在本例中,從double類型轉(zhuǎn)換為int的過程中,會導致精度損失,大多數(shù)編輯器會給出警告

warning: assignment to 'int' from 'double'

在上面的過程中,為了確保表達式本身的合法性,編譯器對操作數(shù)的類型按照一定的規(guī)則進行了轉(zhuǎn)換。這些轉(zhuǎn)換規(guī)則由編譯器自動執(zhí)行,無需開發(fā)人員介入——有時甚至不需要開發(fā)人員了解。這種轉(zhuǎn)換過程,我們稱之為隱式類型轉(zhuǎn)換implicit type conversion)。

此外,由開發(fā)人員通過編碼的方式進行的類型轉(zhuǎn)換,我們稱之為顯示轉(zhuǎn)換,也叫強制類型轉(zhuǎn)換cast)。

雖然有時候確實需要強制類型轉(zhuǎn)換,但是它們本質(zhì)上是非常危險的。

如果兩個類型之間可以相互轉(zhuǎn)換conversion),則稱這兩個類型相關(guān)。

下面我們分別對這兩種轉(zhuǎn)換方式進行深入的介紹:

1 隱式類型轉(zhuǎn)換

為了理解隱式類型轉(zhuǎn)換,我們需要知道它們在什么時候發(fā)生,以及可能出現(xiàn)哪些類型的轉(zhuǎn)換。

1.1 何時發(fā)生隱式類型轉(zhuǎn)換

  • 在混合類型的表達式中,其操作數(shù)被轉(zhuǎn)換為相同的類型;
  • 用作條件的表達式被轉(zhuǎn)換為bool類型;
  • 用一表達式初始化某個變量,或?qū)⒁槐磉_式賦值給某個變量,則該表達式被轉(zhuǎn)換為該變量類型;
  • 在函數(shù)調(diào)用傳遞實參的過程中,其操作數(shù)被轉(zhuǎn)換為形參的類型。

第一點和第三點已經(jīng)在上面的例子中有過說明,下面我們針對第二點和第四點舉例說明:

bool func(int ival)
{
    bool flag = false;
    if (ival)   //ival轉(zhuǎn)換為bool類型,上述第二點
    {
        flag = true;
    }
    return flag;
}

int main()
{
    double dval = 3.541;
    func(dval); //dval轉(zhuǎn)換為int類型,上述第四點
    return 1;
}

1.2 隱式類型轉(zhuǎn)換的種類和規(guī)則

1.2.1 算數(shù)轉(zhuǎn)換(arithmetic conversion)

算數(shù)轉(zhuǎn)換,保證在執(zhí)行操作數(shù)之前,將二元操作符(如算數(shù)和邏輯操作符)的兩個操作數(shù)轉(zhuǎn)換為同一類型,并使表達式的值也具有相同的類型。

算數(shù)轉(zhuǎn)換規(guī)則的核心,是保護計算值的精度不降低。在規(guī)則的內(nèi)容中,定義了一個類型轉(zhuǎn)換的層次,該層次規(guī)定了操作數(shù)應按什么次序轉(zhuǎn)換為表達式中最寬的類型。

由于要保證計算值的精度,因此這類轉(zhuǎn)換本質(zhì)上依賴于機器對各數(shù)據(jù)類型的具體實現(xiàn)。

整形提升integer promotion)規(guī)則:對于所有比int小的整形,包括char、signed char、unsigned char、short和unsigned short,如果該類型的所有可能的值都能包容在int內(nèi),它們就會被提升為int型,否則,它們將被提升為unsigned int。

如果將bool值提升為int,則false轉(zhuǎn)化為0,而true則轉(zhuǎn)化為1。

下面通過例子,我們可以理解的更透徹:

bool flag;
char cval;
short sval;
unsigned short usval;
int ival;
unsigned int uival;
long lval;
unsigned long ulval;
float fval;
double dval;
3.14159L + 'a'; //'a'轉(zhuǎn)換為int,而后再轉(zhuǎn)換為double long
dval + ival;    //ival轉(zhuǎn)換為double
dval + fval;    //fval轉(zhuǎn)換為double
ival = dval;    //dval轉(zhuǎn)換為int(截斷)
flag = dval;    //如果dval是0,那么flag為false,否則為true
cval + fval;    //cval轉(zhuǎn)換為int,而后再轉(zhuǎn)換為float
sval + cval;    //sval和cval都轉(zhuǎn)換為int
cval + lval;    //cval轉(zhuǎn)換為long
ival + ulval;   //ival轉(zhuǎn)換為unsigned long
usval + ival;   //轉(zhuǎn)換取決于unsigned short和int的位寬
uival + lval;   //轉(zhuǎn)換取決于unsigned int和long的位寬

最后兩條語句,單獨說明一下。為了保證計算值的精度不降低,倒數(shù)第二條,如果int型足夠表示所有unsigned short型的值,則將unsigned short轉(zhuǎn)換為int,否則,將兩個操作數(shù)均轉(zhuǎn)換為unsigned int。相同的,對于最后一條語句,如果long型足夠表示所有unsigned int型的值,則將unsigned int轉(zhuǎn)換為long,否則,將兩個操作數(shù)均轉(zhuǎn)換為unsigned long。

1.2.2 指針轉(zhuǎn)換

指針轉(zhuǎn)換主要存在于下列幾種情況中:

  • 使用數(shù)組時,大多數(shù)情況下會將數(shù)組轉(zhuǎn)換為指向第一個元素的指針;
  • 指向任意數(shù)據(jù)類型的指針都可轉(zhuǎn)換為void*類型;
  • 整型數(shù)值常量0可轉(zhuǎn)換為任意指針類型(轉(zhuǎn)換后為空指針)。

對于第一條,在下列幾種情況下,不會進行類型轉(zhuǎn)換:

  • 數(shù)組用作取地址操作符(&)的操作數(shù)時;
  • 數(shù)組用作sizeof操作符的操作數(shù)時;
  • 用數(shù)組對數(shù)組的引用進行初始化時。

1.2.3 轉(zhuǎn)換為bool類型

算數(shù)值和指針都可以轉(zhuǎn)換為bool類型。以下幾種情況,轉(zhuǎn)換的bool值為false:

  • 算數(shù)值為0;
  • 指針值為NULL;
  • char型值為空字符(null)。

本質(zhì)上,上述情況都可以轉(zhuǎn)換為算數(shù)值0。其他情況下,均為true。

1.2.4 轉(zhuǎn)換與枚舉類型

C++自動將枚舉類型的對象或枚舉成員轉(zhuǎn)換為整型,其轉(zhuǎn)換結(jié)果可用于任何要求使用整數(shù)值的地方。

將enum對象或枚舉成員提升為什么類型,由機器實現(xiàn)枚舉成員最大值共同決定,且該類型至少是int

1.2.5 轉(zhuǎn)換為const對象

  • 使用非const對象初始化const對象的引用;
  • 可以將非const對象的地址(或指針)轉(zhuǎn)換為指向相關(guān)const類型的指針。
int i;
const int &j = i;   //第一種情況

int ci = 0;
const int *p = &ci; //第二種情況

1.2.6 由標準庫類型定義的轉(zhuǎn)換

類類型可以定義由編譯器自動執(zhí)行的類型轉(zhuǎn)換。該部分超出本章的討論范圍,將會在C++類的介紹中進行描述。

2 強制類型轉(zhuǎn)換

建議:避免使用強制類型轉(zhuǎn)換,因為其關(guān)閉或掛起了正常的類型檢查。

2.1 何時需要強制類型轉(zhuǎn)換

  • 覆蓋通常的標準轉(zhuǎn)換;
  • 可能存在多種轉(zhuǎn)換時,需要選擇一種特定的轉(zhuǎn)換類型。

2.2 命名的強制類型轉(zhuǎn)換

其一般形式如下:

cast-name<type>(expression);
  • cast-name為static_cast、dynamic_cast、const_cast和reinterpret_cast之一;
  • type為轉(zhuǎn)換的類型目標;
  • expression為被強制轉(zhuǎn)換的值。

強制轉(zhuǎn)換的類型,指定了在expression上執(zhí)行某種特定類型的轉(zhuǎn)換:

2.2.1 dynamic_cast

支持運行時識別指針或引用所指向的對象。

2.2.2 const_cast

顧名思義,可以轉(zhuǎn)換掉表達式的const性質(zhì)。例如:

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

這里,假設有函數(shù)string_copy,我們對其唯一的char* 類型參數(shù)只讀不寫。當我們要傳入的實參是const char* 類型時,可以通過const_cast轉(zhuǎn)換掉參數(shù)的const性質(zhì)。

2.2.3 static_cast

編譯器隱式執(zhí)行的任何類型轉(zhuǎn)換都可以有static_cast顯示完成;如果編譯器不提供自動轉(zhuǎn)換,也可以通過static_cast來實現(xiàn)。

//前一種情況
double d = 97.0;
char ch = static_cast<char>(d); //編譯器不再報精度丟失的錯誤

//后一種情況
void* p = &d;
double *dp = static_cast<double*>(p);

2.2.4 reinterpret_cast

通常為操作數(shù)的位模式提供較低層次的重新解釋。

int *ip;
char* pc = reinterpret_cast<char*>(ip);

2.3 舊式強制類型轉(zhuǎn)換

在引入命名的強制類型轉(zhuǎn)換操作符之前,顯示強制轉(zhuǎn)換用圓括號將類型括起來實現(xiàn):

type (expression);  //函數(shù)風格的強制類型轉(zhuǎn)換寫法
(type) expression;  //C語言風格的強制類型轉(zhuǎn)換寫法

與舊式相比較,命名的強制類型轉(zhuǎn)換的優(yōu)點是:加強強制類型轉(zhuǎn)換的可視性,開發(fā)人員可清晰辨別每個強制類型轉(zhuǎn)換潛在的風險級別。

支持舊式強制轉(zhuǎn)換符號,是為了對“在標準C++之前編寫的程序”保持向后兼容性,并保持與C語言的兼容性。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容