相關(guān)章節(jié)
「C++類的特殊成員函數(shù)(1):構(gòu)造函數(shù)」中“3 隱式類類型轉(zhuǎn)換”
考慮下面的例子:
int ival = 0;
ival = 3.541 + 3; //編譯器會(huì)給出警告
在計(jì)算ival的值時(shí)了袁,C++不會(huì)把兩個(gè)不同類型的值直接相加乏冀,而是提供了一套轉(zhuǎn)換規(guī)則派哲,在做加法計(jì)算前瓣铣,先將兩個(gè)操作數(shù)轉(zhuǎn)換為同一種數(shù)據(jù)類型。
這里ival最終的值為6续镇。在賦值操作中岂丘,不可能更改左操作數(shù)對(duì)象的類型陵究。如果賦值操作符的左右操作數(shù)類型不同,那么右操作數(shù)會(huì)被轉(zhuǎn)換為左操作數(shù)的類型奥帘。
在本例中铜邮,從double類型轉(zhuǎn)換為int的過程中,會(huì)導(dǎo)致精度損失寨蹋,大多數(shù)編輯器會(huì)給出警告:
warning: assignment to 'int' from 'double'
在上面的過程中松蒜,為了確保表達(dá)式本身的合法性,編譯器對(duì)操作數(shù)的類型按照一定的規(guī)則進(jìn)行了轉(zhuǎn)換已旧。這些轉(zhuǎn)換規(guī)則由編譯器自動(dòng)執(zhí)行秸苗,無需開發(fā)人員介入——有時(shí)甚至不需要開發(fā)人員了解。這種轉(zhuǎn)換過程运褪,我們稱之為隱式類型轉(zhuǎn)換(implicit type conversion)惊楼。
此外玖瘸,由開發(fā)人員通過編碼的方式進(jìn)行的類型轉(zhuǎn)換,我們稱之為顯示轉(zhuǎn)換檀咙,也叫強(qiáng)制類型轉(zhuǎn)換(cast)店读。
雖然有時(shí)候確實(shí)需要強(qiáng)制類型轉(zhuǎn)換,但是它們本質(zhì)上是非常危險(xiǎn)的攀芯。
如果兩個(gè)類型之間可以相互轉(zhuǎn)換(conversion),則稱這兩個(gè)類型相關(guān)文虏。
下面我們分別對(duì)這兩種轉(zhuǎn)換方式進(jìn)行深入的介紹:
1 隱式類型轉(zhuǎn)換
為了理解隱式類型轉(zhuǎn)換侣诺,我們需要知道它們?cè)谑裁磿r(shí)候發(fā)生,以及可能出現(xiàn)哪些類型的轉(zhuǎn)換氧秘。
1.1 何時(shí)發(fā)生隱式類型轉(zhuǎn)換
- 在混合類型的表達(dá)式中年鸳,其操作數(shù)被轉(zhuǎn)換為相同的類型;
- 用作條件的表達(dá)式被轉(zhuǎn)換為bool類型丸相;
- 用一表達(dá)式初始化某個(gè)變量搔确,或?qū)⒁槐磉_(dá)式賦值給某個(gè)變量,則該表達(dá)式被轉(zhuǎn)換為該變量類型灭忠;
- 在函數(shù)調(diào)用傳遞實(shí)參的過程中膳算,其操作數(shù)被轉(zhuǎn)換為形參的類型。
第一點(diǎn)和第三點(diǎn)已經(jīng)在上面的例子中有過說明弛作,下面我們針對(duì)第二點(diǎn)和第四點(diǎn)舉例說明:
bool func(int ival)
{
bool flag = false;
if (ival) //ival轉(zhuǎn)換為bool類型涕蜂,上述第二點(diǎn)
{
flag = true;
}
return flag;
}
int main()
{
double dval = 3.541;
func(dval); //dval轉(zhuǎn)換為int類型,上述第四點(diǎn)
return 1;
}
1.2 隱式類型轉(zhuǎn)換的種類和規(guī)則
1.2.1 算數(shù)轉(zhuǎn)換(arithmetic conversion)
算數(shù)轉(zhuǎn)換映琳,保證在執(zhí)行操作數(shù)之前机隙,將二元操作符(如算數(shù)和邏輯操作符)的兩個(gè)操作數(shù)轉(zhuǎn)換為同一類型,并使表達(dá)式的值也具有相同的類型萨西。
算數(shù)轉(zhuǎn)換規(guī)則的核心有鹿,是保護(hù)計(jì)算值的精度不降低。在規(guī)則的內(nèi)容中谎脯,定義了一個(gè)類型轉(zhuǎn)換的層次葱跋,該層次規(guī)定了操作數(shù)應(yīng)按什么次序轉(zhuǎn)換為表達(dá)式中最寬的類型。
由于要保證計(jì)算值的精度穿肄,因此這類轉(zhuǎn)換本質(zhì)上依賴于機(jī)器對(duì)各數(shù)據(jù)類型的具體實(shí)現(xiàn)年局。
整形提升(integer promotion)規(guī)則:對(duì)于所有比int小的整形,包括char咸产、signed char矢否、unsigned char、short和unsigned short脑溢,如果該類型的所有可能的值都能包容在int內(nèi)僵朗,它們就會(huì)被提升為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(截?cái)啵?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的位寬
最后兩條語(yǔ)句藤巢,單獨(dú)說明一下搞莺。為了保證計(jì)算值的精度不降低,倒數(shù)第二條掂咒,如果int型足夠表示所有unsigned short型的值才沧,則將unsigned short轉(zhuǎn)換為int,否則绍刮,將兩個(gè)操作數(shù)均轉(zhuǎn)換為unsigned int温圆。相同的,對(duì)于最后一條語(yǔ)句孩革,如果long型足夠表示所有unsigned int型的值捌木,則將unsigned int轉(zhuǎn)換為long,否則嫉戚,將兩個(gè)操作數(shù)均轉(zhuǎn)換為unsigned long刨裆。
1.2.2 指針轉(zhuǎn)換
指針轉(zhuǎn)換主要存在于下列幾種情況中:
- 使用數(shù)組時(shí),大多數(shù)情況下會(huì)將數(shù)組轉(zhuǎn)換為指向第一個(gè)元素的指針彬檀;
- 指向任意數(shù)據(jù)類型的指針都可轉(zhuǎn)換為void*類型帆啃;
- 整型數(shù)值常量0可轉(zhuǎn)換為任意指針類型(轉(zhuǎn)換后為空指針)。
對(duì)于第一條窍帝,在下列幾種情況下努潘,不會(huì)進(jìn)行類型轉(zhuǎn)換:
- 數(shù)組用作取地址操作符(&)的操作數(shù)時(shí);
- 數(shù)組用作sizeof操作符的操作數(shù)時(shí)坤学;
- 用數(shù)組對(duì)數(shù)組的引用進(jìn)行初始化時(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++自動(dòng)將枚舉類型的對(duì)象或枚舉成員轉(zhuǎn)換為整型,其轉(zhuǎn)換結(jié)果可用于任何要求使用整數(shù)值的地方栖忠。
將enum對(duì)象或枚舉成員提升為什么類型崔挖,由機(jī)器實(shí)現(xiàn)和枚舉成員最大值共同決定,且該類型至少是int庵寞。
1.2.5 轉(zhuǎn)換為const對(duì)象
- 使用非const對(duì)象初始化const對(duì)象的引用虚汛;
- 可以將非const對(duì)象的地址(或指針)轉(zhuǎn)換為指向相關(guān)const類型的指針。
int i;
const int &j = i; //第一種情況
int ci = 0;
const int *p = &ci; //第二種情況
1.2.6 由標(biāo)準(zhǔn)庫(kù)類型定義的轉(zhuǎn)換
類類型可以定義由編譯器自動(dòng)執(zhí)行的類型轉(zhuǎn)換皇帮。該部分超出本章的討論范圍,將會(huì)在C++類的介紹中進(jìn)行描述蛋辈。
2 強(qiáng)制類型轉(zhuǎn)換
建議:避免使用強(qiáng)制類型轉(zhuǎn)換属拾,因?yàn)槠潢P(guān)閉或掛起了正常的類型檢查。
2.1 何時(shí)需要強(qiáng)制類型轉(zhuǎn)換
- 覆蓋通常的標(biāo)準(zhǔn)轉(zhuǎn)換冷溶;
- 可能存在多種轉(zhuǎn)換時(shí)渐白,需要選擇一種特定的轉(zhuǎn)換類型。
2.2 命名的強(qiáng)制類型轉(zhuǎn)換
其一般形式如下:
cast-name<type>(expression);
- cast-name為static_cast逞频、dynamic_cast纯衍、const_cast和reinterpret_cast之一;
- type為轉(zhuǎn)換的類型目標(biāo)苗胀;
- expression為被強(qiáng)制轉(zhuǎn)換的值襟诸。
強(qiáng)制轉(zhuǎn)換的類型,指定了在expression上執(zhí)行某種特定類型的轉(zhuǎn)換:
2.2.1 dynamic_cast
支持運(yùn)行時(shí)識(shí)別指針或引用所指向的對(duì)象基协。
2.2.2 const_cast
顧名思義歌亲,可以轉(zhuǎn)換掉表達(dá)式的const性質(zhì)。例如:
const char *pc_str;
char* pc = string_copy(const_cast<char*>(pc_str));
這里澜驮,假設(shè)有函數(shù)string_copy陷揪,我們對(duì)其唯一的char* 類型參數(shù)只讀不寫。當(dāng)我們要傳入的實(shí)參是const char* 類型時(shí)杂穷,可以通過const_cast轉(zhuǎn)換掉參數(shù)的const性質(zhì)悍缠。
2.2.3 static_cast
編譯器隱式執(zhí)行的任何類型轉(zhuǎn)換都可以有static_cast顯示完成;如果編譯器不提供自動(dòng)轉(zhuǎn)換耐量,也可以通過static_cast來實(shí)現(xiàn)飞蚓。
//前一種情況
double d = 97.0;
char ch = static_cast<char>(d); //編譯器不再報(bào)精度丟失的錯(cuò)誤
//后一種情況
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 舊式強(qiáng)制類型轉(zhuǎn)換
在引入命名的強(qiáng)制類型轉(zhuǎn)換操作符之前玷坠,顯示強(qiáng)制轉(zhuǎn)換用圓括號(hào)將類型括起來實(shí)現(xiàn):
type (expression); //函數(shù)風(fēng)格的強(qiáng)制類型轉(zhuǎn)換寫法
(type) expression; //C語(yǔ)言風(fēng)格的強(qiáng)制類型轉(zhuǎn)換寫法
與舊式相比較蜗搔,命名的強(qiáng)制類型轉(zhuǎn)換的優(yōu)點(diǎn)是:加強(qiáng)強(qiáng)制類型轉(zhuǎn)換的可視性,開發(fā)人員可清晰辨別每個(gè)強(qiáng)制類型轉(zhuǎn)換潛在的風(fēng)險(xiǎn)級(jí)別八堡。
支持舊式強(qiáng)制轉(zhuǎn)換符號(hào)樟凄,是為了對(duì)“在標(biāo)準(zhǔn)C++之前編寫的程序”保持向后兼容性,并保持與C語(yǔ)言的兼容性兄渺。