C++ Primer 筆記-第4章 表達(dá)式

4.1 基礎(chǔ)
4.1.1 基本概念
當(dāng)一個(gè)對(duì)象被用作右值時(shí)的時(shí)用的是對(duì)象的值(內(nèi)容),當(dāng)對(duì)象被用作左值的時(shí)用的是對(duì)象的身份(在內(nèi)存中的位置)。
4.1.3 求值順序
運(yùn)算對(duì)象的求值順序與優(yōu)先級(jí)和結(jié)合律無關(guān)。
如果改變了某運(yùn)算對(duì)象的值,在表達(dá)式的其他地方不要再使用這個(gè)運(yùn)算對(duì)象。(當(dāng)改變運(yùn)算對(duì)象的子表達(dá)式本身就是另外一個(gè)子表達(dá)式的運(yùn)算對(duì)象時(shí)該規(guī)則無效)
4.2 算術(shù)運(yùn)算符
算術(shù)運(yùn)算符的運(yùn)算對(duì)象和求值結(jié)果都是右值。
在取余運(yùn)算中,?m?和?n?是整數(shù)且?n?非?0,除了?-m?導(dǎo)致溢出的特殊情況,其他時(shí)候?m%(-n)?等于?m%n,?(-m)%n?等于?-(m%n)。
4.4 賦值運(yùn)算符

? ?// 錯(cuò)誤:窄化轉(zhuǎn)換
? ?int k = 0; k = {3.14};

因?yàn)橘x值運(yùn)算符的優(yōu)先級(jí)低于關(guān)系運(yùn)算符的優(yōu)先級(jí),所以在條件語句中,賦值部分通常應(yīng)該加括號(hào)。
4.5 遞增和遞減運(yùn)算符
這兩種運(yùn)算符必須作用于左值運(yùn)算對(duì)象。前置版本將對(duì)象本身作為左值返回,后置版本則將對(duì)象原始值的副本作為右值返回。
使用后置版本,對(duì)于整數(shù)和指針類型來說,編譯器可能對(duì)這種額外的工作進(jìn)行一定的優(yōu)化。但是對(duì)于復(fù)雜的迭代器類型,這種額外的工作就消耗巨大。建議養(yǎng)成使用前置版本的習(xí)慣。
后置運(yùn)算符的優(yōu)先級(jí)高于解引用運(yùn)算符,因此?
*pbeg++
?等價(jià)于?*(pbeg++)
。大多數(shù)運(yùn)算符都沒有規(guī)定運(yùn)算對(duì)象的求值順序。下列代碼將產(chǎn)生未定義的行為:

? ?while (beg != s.end()) ? ?
? ?*beg = toupper(*beg++); ? ? // 錯(cuò)誤:該賦值語句未定義
? ?// 編譯器可能按照下面的任意一種思路處理該表達(dá)式:
? ?*beg = toupper(*beg); ? ? ? ? ? // 如果先求左側(cè)的值
? ?*(beg + 1) = toupper(*beg); ? ? // 如果先求右側(cè)的值

4.6 成員訪問運(yùn)算符
由于解引用運(yùn)算符的優(yōu)先級(jí)低于點(diǎn)運(yùn)算符,所以執(zhí)行解引用運(yùn)算的子表達(dá)式兩端必須加上括號(hào)。

? ?ptr->mem;
? ?(*ptr).mem; // 等價(jià)
? ?*ptr.mem; ? // 不等價(jià)

箭頭運(yùn)算符作用于一個(gè)指針類型的運(yùn)算對(duì)象,結(jié)果是一個(gè)左值。
點(diǎn)運(yùn)算符分成兩種情況:如果成員所屬的對(duì)象是左值,那么結(jié)果是左值;反之,如果成員的對(duì)象是右值,那么結(jié)果是右值。
4.7 條件運(yùn)算符
當(dāng)條件運(yùn)算符?
(cond ? expr1 : expr2)
?的兩個(gè)表達(dá)式都是左值或者能轉(zhuǎn)換成同一種左值類型時(shí),運(yùn)算的結(jié)果是左值;否則運(yùn)算的結(jié)果是右值。

? ?int a = 0, b = 0, c = 1;
? ?bool d = false;
? ?d? a : b = c; ? ? ? // 運(yùn)算結(jié)果是左值
? ?c = d? 0 : 1; ? ? ? // 運(yùn)算結(jié)果是右值

條件運(yùn)算符的優(yōu)先級(jí)非常低,因此當(dāng)一條長(zhǎng)表達(dá)式中嵌套了條件運(yùn)算于表達(dá)式時(shí),通常需要在它兩端加上括號(hào)。

? ?// 正確表達(dá),輸出 pass 或者 fail
? ?cout << ((grade < 60) ? "fail" : "pass");
? ?// 錯(cuò)誤表達(dá),輸出 1 或者 0!
? ?cout << (grade < 60) ? "fail" : "pass";
? ?// 其表達(dá)等價(jià)于:
? ?cout << (grade < 60); ? ? ? // 輸出 1 或 0
? ?cout ? "fail" : "pass"; ? ? // 根據(jù) cout 的值的真假產(chǎn)生對(duì)應(yīng)的字面值
? ?// 錯(cuò)誤表達(dá),試圖比較 cout 和 60
? ?cout << grade < 60 ? "fail" : "pass";
? ?// 其表達(dá)等價(jià)于
? ?cout << grade; ? ? // 小于運(yùn)算符的優(yōu)先級(jí)低于位移運(yùn)算符,所以先輸出grade
? ?cout < 60 ? "fail" : "pass"; ? ?// 比較 cout 和 60!

4.8 位運(yùn)算符
如果運(yùn)算對(duì)象是“小整型”,則它的值會(huì)被自動(dòng)提升成較大的整數(shù)類型。
如果運(yùn)算對(duì)象是帶符號(hào)的且它的值為負(fù),那么位運(yùn)算符如何處理運(yùn)算對(duì)象的“符號(hào)位”依賴于機(jī)器。
關(guān)于符號(hào)位如何處理沒有明確規(guī)定,所以強(qiáng)烈建議僅將位運(yùn)算符用于處理無符號(hào)類型。
移位運(yùn)算符?
<<
?和?>>
?右側(cè)的運(yùn)算對(duì)象一定不能為負(fù),而且值必須嚴(yán)格小于結(jié)果的位數(shù),否則會(huì)發(fā)生未定義行為。移位運(yùn)算符的優(yōu)先級(jí)不高不低,介于中間:比算術(shù)運(yùn)算符的優(yōu)先級(jí)低,但是比關(guān)系運(yùn)算符、賦值運(yùn)算符和條件運(yùn)算符的優(yōu)先級(jí)高。
4.9?sizeof
?運(yùn)算符
在?
sizeof
?的運(yùn)算對(duì)象中,解引用一個(gè)無效指針仍然是一種安全的行為,因?yàn)橹羔槍?shí)際上并沒有被真正使用。sizeof
?不需要真的解引用指針也能知道它所指對(duì)象的類型。對(duì)?
char
?或者類型為?char
?的表達(dá)式執(zhí)行?sizeof
?運(yùn)算,結(jié)果為1。對(duì)數(shù)組執(zhí)行?
sizeof
?運(yùn)算得到整個(gè)數(shù)組所占空間的大小,該運(yùn)算不會(huì)把數(shù)組轉(zhuǎn)換成指針來處理。對(duì)?
string
?對(duì)象或?vector
?對(duì)象執(zhí)行?sizeof
?運(yùn)算只返回該類型固定部分的大小,不會(huì)計(jì)算對(duì)象的元素占用了多少空間。
4.10 逗號(hào)運(yùn)算符
對(duì)于逗號(hào)運(yùn)算符來說,首先對(duì)左側(cè)的表達(dá)式求值,然后將求值結(jié)果丟棄掉。運(yùn)算符真正的結(jié)果是右側(cè)表達(dá)式的值。
4.11 類型運(yùn)算符
4.11.1 算術(shù)轉(zhuǎn)換
某個(gè)運(yùn)算符的運(yùn)算對(duì)象類型不一致,一個(gè)對(duì)象是無符號(hào)類型,一個(gè)是有符號(hào)類型,而且其中的無符號(hào)類型不小于帶符號(hào)類型,那么帶符號(hào)的運(yùn)算對(duì)象轉(zhuǎn)換成無符號(hào)的。
如果帶符號(hào)類型大于無符號(hào)類型,此時(shí)轉(zhuǎn)換的結(jié)果依賴于機(jī)器。如果無符號(hào)類型的所有值都能存在該帶符號(hào)類型中,則無符號(hào)類型的運(yùn)算對(duì)象轉(zhuǎn)換為帶符號(hào)類型。如果不能,那么帶符號(hào)類型的運(yùn)算對(duì)象轉(zhuǎn)換成無符號(hào)類型。
4.11.3 顯示轉(zhuǎn)換
static_cast
:任何具有明確定義的類型轉(zhuǎn)換,只要不包含底層?const
,都可以使用?static_cast
??梢允褂?static_cast
?找回存在于?void*
?指針中的值。const_cast
:只能改變運(yùn)算對(duì)象的底層?const
。reinterpret_cast
?通常為運(yùn)算對(duì)象的位模式提供較低層次上的重新解釋:(1) 使用?
reinterpret_cast
?是非常危險(xiǎn)的,其中的關(guān)鍵問題是類型改變了,但是編譯器卻沒有給出任何警告或者錯(cuò)誤的提示信息。(2)?
reinterpret_cast
?本質(zhì)上依賴于機(jī)器。想要安全的使用必須對(duì)涉及的類型和編譯器實(shí)現(xiàn)轉(zhuǎn)換的過程都非常熟悉。強(qiáng)制類型轉(zhuǎn)換干擾了正常的類型檢查,強(qiáng)烈建議程序員避免使用。實(shí)在無法避免的時(shí)候應(yīng)該盡量限制類型轉(zhuǎn)換值的作用域,并且記錄對(duì)相關(guān)類型的所以假定,這樣可以減少錯(cuò)誤發(fā)生的機(jī)會(huì)。
舊式的強(qiáng)制類型轉(zhuǎn)換:

? ?type (expr); ? ? ? ?// 函數(shù)形式的強(qiáng)制類型轉(zhuǎn)換
? ?(type) expr; ? ? ? ?// C語言風(fēng)格的強(qiáng)制類型轉(zhuǎn)換


