4.1 c++左值和右值、类型转换
左值和右值
- c++的表達(dá)式不是右值就是左值。
- 一個(gè)左值表達(dá)式的求值結(jié)果是一個(gè)對(duì)象或一個(gè)函數(shù),然而以常量對(duì)象為代表的某些左值實(shí)際上不能作為賦值語(yǔ)句的左側(cè)運(yùn)算對(duì)象。此外,雖然某些表達(dá)式的求值結(jié)果是對(duì)象,但它們是右值而非左值。
- 當(dāng)一個(gè)對(duì)象被用作右值時(shí),用的是對(duì)象的值(內(nèi)容),當(dāng)對(duì)象被用作左值的時(shí)候,用的是對(duì)象的身份(在內(nèi)存中的位置)
- 一個(gè)重要的原則是在需要右值的地方可以用左值來(lái)代替,但是不能把右值當(dāng)作左值(也就是位置)使用。當(dāng)一個(gè)左值被當(dāng)作右值使用時(shí),實(shí)際使用的是它的內(nèi)容(值)。 - 賦值運(yùn)算符需要一個(gè)左值作為其左側(cè)運(yùn)算對(duì)象,得到的結(jié)果也仍然是一個(gè)左值。
- 取地址符作用于一個(gè)左值運(yùn)算對(duì)象,返回一個(gè)指向該運(yùn)算對(duì)象的指針,這個(gè)指針是一個(gè)右值。
- 內(nèi)置解引用運(yùn)算符、下標(biāo)運(yùn)算符、迭代器解引用運(yùn)算符、string和vector的下標(biāo)運(yùn)算符的求值結(jié)果都是左值。
- 內(nèi)置類(lèi)型和迭代器的遞增遞減運(yùn)算符作用于左值運(yùn)算對(duì)象,其前置版本所得的結(jié)果也是左值。
 
- 使用關(guān)鍵字decltype時(shí),左值和右值也各有不同。如果表達(dá)式的求值結(jié)果是左值,decltype作用于該表達(dá)式(不是變量)得到一個(gè)引用類(lèi)型。 - 假定p的類(lèi)型是int*,因?yàn)榻庖眠\(yùn)算符生成左值,所以decltype(*p)的結(jié)果是int&。
- 另一方面,因?yàn)槿〉刂愤\(yùn)算符生成右值,所以decltype(&p)的結(jié)果是int**,也就是說(shuō),結(jié)果是一個(gè)指向整型指針的指針。
 
類(lèi)型轉(zhuǎn)換
- 在c++語(yǔ)言中,某些類(lèi)型之間有關(guān)聯(lián)。如果兩種類(lèi)型有關(guān)聯(lián),那么當(dāng)程序需要其中一種類(lèi)型的運(yùn)算對(duì)象時(shí),可以用另一種關(guān)聯(lián)類(lèi)型的對(duì)象或值來(lái)替代。如果兩種類(lèi)型可以相互轉(zhuǎn)換,那么它們就是關(guān)聯(lián)的。
- 加法的兩個(gè)運(yùn)算對(duì)象不同,c++語(yǔ)言不會(huì)直接將兩個(gè)不同類(lèi)型的值相加,而是實(shí)現(xiàn)根據(jù)類(lèi)型轉(zhuǎn)換規(guī)則設(shè)法將運(yùn)算對(duì)象的類(lèi)型統(tǒng)一后再求值。上述的類(lèi)型轉(zhuǎn)換是自動(dòng)執(zhí)行的,因此它們被稱(chēng)作隱式類(lèi)型轉(zhuǎn)換。
- 算術(shù)類(lèi)型之間的隱式類(lèi)型轉(zhuǎn)換被設(shè)計(jì)的盡可能的避免損失精度。很多時(shí)候,如果表達(dá)式中既有整數(shù)類(lèi)型的運(yùn)算對(duì)象,又有浮點(diǎn)數(shù)類(lèi)型的運(yùn)算對(duì)象,整數(shù)會(huì)轉(zhuǎn)換成浮點(diǎn)型。在上例中,3轉(zhuǎn)換成double類(lèi)型,然后執(zhí)行浮點(diǎn)數(shù)加法,所得的結(jié)果類(lèi)型是double
- 接下來(lái)完成初始化,在初始化過(guò)程中,因?yàn)楸怀跏蓟膶?duì)象的類(lèi)型無(wú)法改變,所以初始值被轉(zhuǎn)換成該對(duì)象的類(lèi)型。在上例中,加法得到的double類(lèi)型的結(jié)果轉(zhuǎn)換成int類(lèi)型,這個(gè)值被用來(lái)初始化ival。由double向int轉(zhuǎn)換時(shí)忽略掉了小數(shù)部分,在上述表達(dá)式中,數(shù)值6被賦給了ival。
何時(shí)發(fā)生隱式類(lèi)型轉(zhuǎn)換
- 在大多數(shù)情況下,比int類(lèi)型小的整型值首先提升為較大的整數(shù)類(lèi)型。
- 在條件中,非布爾值轉(zhuǎn)換成布爾類(lèi)型。
- 初始化過(guò)程中,初始值轉(zhuǎn)換成變量的類(lèi)型;在賦值語(yǔ)句中,右側(cè)運(yùn)算對(duì)象轉(zhuǎn)換成左側(cè)運(yùn)算對(duì)象的類(lèi)型
- 如果算術(shù)運(yùn)算或關(guān)系運(yùn)算的運(yùn)算對(duì)象有多種類(lèi)型,需要轉(zhuǎn)換成同一種類(lèi)型。
- 函數(shù)調(diào)用時(shí)也會(huì)發(fā)生類(lèi)型轉(zhuǎn)換。
算術(shù)轉(zhuǎn)換
- 算術(shù)轉(zhuǎn)換的含義是把一種算術(shù)類(lèi)型轉(zhuǎn)換成另一種算術(shù)類(lèi)型。 - 算術(shù)轉(zhuǎn)換的規(guī)則定義了一套類(lèi)型轉(zhuǎn)換的層次,其中運(yùn)算符的運(yùn)算對(duì)象將轉(zhuǎn)換成最寬的類(lèi)型。例如,如果一個(gè)運(yùn)算對(duì)象的類(lèi)型是long double,那么不論另外一個(gè)運(yùn)算對(duì)象的類(lèi)型是什么都會(huì)轉(zhuǎn)換成long double。
- 當(dāng)表達(dá)式中既有浮點(diǎn)類(lèi)型又有整數(shù)類(lèi)型時(shí),整數(shù)類(lèi)型將轉(zhuǎn)換成相應(yīng)的浮點(diǎn)類(lèi)型。
 
整型提升
- 整型提升負(fù)則把小整數(shù)類(lèi)型換成較大的整數(shù)類(lèi)型。對(duì)于bool、char、signed char、unsigned char、short和unsigned short等類(lèi)型來(lái)說(shuō),只要它們所有可能的值都存在int里,它們就會(huì)提升成int類(lèi)型;否則,提升成unsigned int類(lèi)型。
- 較大的char類(lèi)型提升成int、unsigned int、long、unsigned long、long long和unsigned long long中最小的一種類(lèi)型,前提是轉(zhuǎn)換后的類(lèi)型要能容納原類(lèi)型所有可能的值。
無(wú)符號(hào)類(lèi)型的運(yùn)算對(duì)象
- 如果某個(gè)運(yùn)算符的運(yùn)算對(duì)象類(lèi)型不一致,這些運(yùn)算對(duì)象將轉(zhuǎn)換成同一種類(lèi)型。但是如果某個(gè)運(yùn)算對(duì)象的類(lèi)型是無(wú)符號(hào)類(lèi)型,那么轉(zhuǎn)換的結(jié)果就要依賴(lài)于機(jī)器中各個(gè)整數(shù)類(lèi)型的相對(duì)大小了。
- 首先執(zhí)行整型提升。如果結(jié)果的類(lèi)型匹配,無(wú)需進(jìn)行進(jìn)一步的轉(zhuǎn)換。如果兩個(gè)運(yùn)算對(duì)象的類(lèi)型要么都是帶符號(hào)的、要么都是無(wú)符號(hào)的,則小類(lèi)型運(yùn)算對(duì)象轉(zhuǎn)換為較大的類(lèi)型。
- 如果一個(gè)運(yùn)算對(duì)象是無(wú)符號(hào)類(lèi)型、另外一個(gè)運(yùn)算對(duì)象是帶符號(hào)類(lèi)型,而且其中的無(wú)符號(hào)類(lèi)型不小于帶符號(hào)類(lèi)型,那么帶符號(hào)的運(yùn)算對(duì)象轉(zhuǎn)換成無(wú)符號(hào)的。例如,假設(shè)兩個(gè)類(lèi)型分別是unsigned int 和int,則int類(lèi)型的運(yùn)算對(duì)象轉(zhuǎn)換成unsigned int類(lèi)型。需要注意的是,如果int類(lèi)型為負(fù)值,它的轉(zhuǎn)換將出現(xiàn)問(wèn)題。
- 如果帶符號(hào)類(lèi)型大于無(wú)符號(hào)類(lèi)型,此時(shí)的轉(zhuǎn)換結(jié)果依賴(lài)于機(jī)器。如果無(wú)符號(hào)類(lèi)型的所有值都能存在該帶符號(hào)類(lèi)型中,則無(wú)符號(hào)類(lèi)型的運(yùn)算對(duì)象轉(zhuǎn)換成帶符號(hào)類(lèi)型。如果不能,那么帶符號(hào)類(lèi)型的運(yùn)算對(duì)象轉(zhuǎn)換成無(wú)符號(hào)類(lèi)型。例如,如果兩個(gè)運(yùn)算對(duì)象的類(lèi)型分別為long和unsigned int,并且int和long的大小相同,則long類(lèi)型的運(yùn)算對(duì)象轉(zhuǎn)換成unsigned int類(lèi)型;如果long類(lèi)型占用空間比int更多,則unsigned int類(lèi)型的運(yùn)算對(duì)象轉(zhuǎn)換成long。
其它隱式類(lèi)型轉(zhuǎn)換
- 數(shù)組轉(zhuǎn)換成指針:在大多數(shù)用到數(shù)組的表達(dá)式中,數(shù)組自動(dòng)轉(zhuǎn)換成指向數(shù)組首元素的指針
-  當(dāng)數(shù)組被用作decltype關(guān)鍵字的參數(shù),或者作為取地址符&、sizeof及typeid等運(yùn)算符的運(yùn)算對(duì)象時(shí),上述轉(zhuǎn)換不會(huì)發(fā)生。 
-  如果用一個(gè)引用來(lái)初始化數(shù)組,上述轉(zhuǎn)換也不會(huì)發(fā)生。 
-  指針的轉(zhuǎn)換:c++還規(guī)定了幾種其他的指針轉(zhuǎn)換方式,包括常量整數(shù)值0或者字面值nullptr能轉(zhuǎn)換成任意指針類(lèi)型;指向任意非常量的指針能轉(zhuǎn)換成void*;指向任意對(duì)象的指針能轉(zhuǎn)換成const void*. 
-  在有繼承關(guān)系的類(lèi)型間還有另外一種指針轉(zhuǎn)換的方式。 
-  轉(zhuǎn)換成布爾類(lèi)型:存在一種從算術(shù)類(lèi)型或指針類(lèi)型向布爾類(lèi)型自動(dòng)轉(zhuǎn)換的機(jī)制。如果指針或算術(shù)類(lèi)型的值為0,轉(zhuǎn)換結(jié)果為false;否則轉(zhuǎn)換結(jié)果為true 
-  轉(zhuǎn)換成常量:允許將指向非常量類(lèi)型的指針轉(zhuǎn)換成指向相應(yīng)的常量類(lèi)型的指針,對(duì)于引用也是這樣。也就是說(shuō),如果T是一種類(lèi)型,就能將指向T的指針或引用分別轉(zhuǎn)換成指向const T的指針或引用。 
-  相反的轉(zhuǎn)換并不存在,因?yàn)樗噲D刪除掉底層const 
-  類(lèi)類(lèi)型定義的轉(zhuǎn)換:類(lèi)類(lèi)型能定義由編譯器自動(dòng)執(zhí)行的轉(zhuǎn)換,不過(guò)編譯器每次只能執(zhí)行一種類(lèi)類(lèi)型的轉(zhuǎn)換。如果同時(shí)提出多個(gè)轉(zhuǎn)換請(qǐng)求,這些請(qǐng)求將被拒絕。 
顯式轉(zhuǎn)換
- 有時(shí)希望顯式地將對(duì)象強(qiáng)制轉(zhuǎn)換成另外一種類(lèi)型,這種方法稱(chēng)作強(qiáng)制類(lèi)型轉(zhuǎn)換。
命名的強(qiáng)制類(lèi)型轉(zhuǎn)換
- 一個(gè)命名的強(qiáng)制類(lèi)型轉(zhuǎn)換具有如下形式
- 其中,type是轉(zhuǎn)換的目標(biāo)類(lèi)型,而expression是要轉(zhuǎn)換的值。如果type是引用類(lèi)型,則結(jié)果是左值。
- cast-name是static_cast、dynamic_cast、const_cast和reinterpret_cast中的一種。dynamic_cast支持運(yùn)行時(shí)類(lèi)型識(shí)別,cast_name指定了執(zhí)行的是哪種轉(zhuǎn)換。
ststic_cast
- 任何具有明確定義的類(lèi)型轉(zhuǎn)換,只要不包含底層const,都可以使用static_cast.
- 當(dāng)需要把一個(gè)較大的算術(shù)類(lèi)型賦值給較小的類(lèi)型時(shí),static_cast非常有用。此時(shí),強(qiáng)制類(lèi)型轉(zhuǎn)換告訴編譯器,我們知道且不在乎潛在的精度損失。一般來(lái)說(shuō),如果編譯器發(fā)現(xiàn)了一個(gè)較大的算術(shù)類(lèi)型試圖賦值給較小的類(lèi)型,就會(huì)給出警告信息;但是當(dāng)執(zhí)行了顯式類(lèi)型轉(zhuǎn)換后,警告信息就會(huì)被關(guān)閉了。
- static_cast對(duì)于編譯器無(wú)法自動(dòng)執(zhí)行的類(lèi)型轉(zhuǎn)換非常有用。例如,可以使用static_cast找回存在于void*指針中的值。
- 把指針存放在void*中,且使用static_cast將其強(qiáng)制轉(zhuǎn)換回原來(lái)的類(lèi)型時(shí),應(yīng)該確保指針的值保持不變。也就是說(shuō),強(qiáng)制類(lèi)型轉(zhuǎn)換的原地址相等,因此必需確保轉(zhuǎn)換后所得的類(lèi)型就是指針?biāo)傅念?lèi)型。類(lèi)型一旦不符,將產(chǎn)生未定義的后果。
const_cast
- const_cast只能改變運(yùn)算對(duì)象的底層const
- 對(duì)于將常量對(duì)象轉(zhuǎn)換成非常量對(duì)象的行為,一般稱(chēng)其為"去掉const性質(zhì)"。一旦去掉了某個(gè)對(duì)象的const性質(zhì),編譯器就不再阻止我們對(duì)該對(duì)象進(jìn)行寫(xiě)操作了。如果對(duì)象本身不是一個(gè)常量,使用強(qiáng)制類(lèi)型轉(zhuǎn)換獲得寫(xiě)權(quán)限是合法的行為。然而如果對(duì)象是一個(gè)常量,再使用const_cast執(zhí)行寫(xiě)操作就會(huì)產(chǎn)生未定義的后果。
- 只有const_cast能改變表達(dá)式的常量屬性,使用其它形式的命名強(qiáng)制類(lèi)型轉(zhuǎn)換改變表達(dá)式的常量屬性都將引發(fā)編譯器錯(cuò)誤。同樣的,也不能用const_cast改變表達(dá)式的類(lèi)型
- const_cast常常用于有函數(shù)重載的上下文中。
reinterpreter_cast
- reinterpret_cast通常為運(yùn)算對(duì)象的位模式提供較低層次上的重新解釋。假設(shè)有如下轉(zhuǎn)換:
- pc所指的真實(shí)對(duì)象是一個(gè)int而非字符,如果把pc當(dāng)成普通的字符指針使用就可能再運(yùn)行時(shí)發(fā)生錯(cuò)誤,例如:
- 可能導(dǎo)致異常的運(yùn)行時(shí)行為。
- 使用reinterpret_cast是非常危險(xiǎn)的,用pc初始化str的例子很好的證明了這一點(diǎn)。其中關(guān)鍵問(wèn)題是類(lèi)型改變了,但編譯器沒(méi)有給出任何警告或錯(cuò)誤的提示信息。
- 當(dāng)用一個(gè)int的地址初始化pc時(shí),由于顯式的聲稱(chēng)這種轉(zhuǎn)換合法,所以編譯器沒(méi)法知道它實(shí)際存放的是指向int的指針。最終的結(jié)果是,在上例中雖然用pc初始化str沒(méi)什么實(shí)際意義,甚至還可能引發(fā)更糟糕的后果,但僅從語(yǔ)法上而言,這種操作沒(méi)錯(cuò)。
- 查找這類(lèi)問(wèn)題的原因非常困難,如果將ip強(qiáng)制轉(zhuǎn)換成pc的語(yǔ)句和用pc初始化string對(duì)象的語(yǔ)句分屬不同文件就更是如此。
- reinterpret_cast本質(zhì)上依賴(lài)于機(jī)器。要想安全的使用reinterpret_cast必需對(duì)涉及的類(lèi)型和編譯器首先的轉(zhuǎn)換過(guò)程非常了解。
建議:避免強(qiáng)制類(lèi)型轉(zhuǎn)換
 強(qiáng)制類(lèi)型轉(zhuǎn)換干擾了正常的類(lèi)型檢查,強(qiáng)烈建議程序員避免使用強(qiáng)制類(lèi)型轉(zhuǎn)換。在有重載函數(shù)的上下文中使用const_cast無(wú)可厚非,但在其他情況下使用conat_cast也就意味著程序的某種設(shè)計(jì)缺陷。其它強(qiáng)制類(lèi)型轉(zhuǎn)換,都應(yīng)該反復(fù)斟酌能否以其他方式實(shí)現(xiàn)相同的目標(biāo)。就算實(shí)在無(wú)法避免,也應(yīng)該盡量限制類(lèi)型轉(zhuǎn)換的作用域,并且記錄對(duì)相關(guān)類(lèi)型的所有假定,這樣可以減少錯(cuò)誤發(fā)生的機(jī)會(huì)。
舊式的強(qiáng)制類(lèi)型轉(zhuǎn)換
- 在早期版本的c++語(yǔ)言中,顯式的進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換包含兩種形式
- 根據(jù)所涉及的類(lèi)型不同,舊式的強(qiáng)制類(lèi)型轉(zhuǎn)換分別具有與const_cast、static_cast和reinterpret_cast相似的行為。在某處執(zhí)行舊式的強(qiáng)制類(lèi)型轉(zhuǎn)換時(shí),如果換成const_cast和static_cast也合法,則其行為與對(duì)應(yīng)的命名轉(zhuǎn)換一致。如果替換后不合法,則舊式強(qiáng)制類(lèi)型轉(zhuǎn)換執(zhí)行與reinterpret_cast類(lèi)似的功能的效果與使用reinterpret_cast一樣
與命名的強(qiáng)制類(lèi)型轉(zhuǎn)換相比,舊式的強(qiáng)制類(lèi)型轉(zhuǎn)換從表現(xiàn)形式上來(lái)說(shuō)不那么清晰明了,所以一旦轉(zhuǎn)換過(guò)程出現(xiàn)問(wèn)題,追蹤起來(lái)也更加困難。
總結(jié)
以上是生活随笔為你收集整理的4.1 c++左值和右值、类型转换的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: RT-Thread 模拟器 simula
- 下一篇: SOC 时钟源PLL锁相环详细介绍——I
