##C++ Primer 學習筆記(第四章:表達式)express
[TOC]數組
###4.1 基礎安全
左值和右值:
當一個對象被用做右值的時候,用的是對象的值(內容);當對象被用做左值的時候,用的是對象的身份(在內存中的位置)。 一個重要的原則是在須要右值的地方能夠用左值來代替,可是不能把右值當成左值(也就是位置)使用。當一個左值被當成右值使用時,實際使用的是它的內容(值)。函數
使用decltype
的時候,若是表達式的求值結果是左值,decltype
做用於該表達式獲得一個引用類型。 p
的類型是int*
,而*p
會生成左值,多以decltype(*p)
會獲得引用int &
。 由於取地址會生成右值,因此decltype(&p)
的結果是int**
。post
對於那些沒有指定執行順序的運算符來講,若是表達式指向並修改了同一個對象(或者執行了IO任務),將會引起錯誤併產生未定義的行爲。例如:學習
int i = 0; cout << i << " " << ++i << endl;
有4種運算符明確規定了運算對象的求值順序:邏輯與&&
(先求左側,左側爲真再求右側)、邏輯或||
、條件運算符?:
、逗號運算符,
。ui
###4.2 算術運算符.net
整數相除的結果仍是整數,若是有小數部分直接棄除。 結果爲負的商統一貫0取整,即直接切除小數部分。指針
參與取模運算%
的運算對象必須是整數。 表達式(m/n)*n+m%n
的結果與m
相同,即m%n
若是不等於0,則它的符號應該和m相同。code
-21 % -8; // -5 -21 / -8; //2 21 % -5; //1 21/ -5; //-4
###4.3 邏輯關係和運算符
短路求值: 邏輯與(&&
):當且僅當左側運算爲真時纔對右側進行求值; 邏輯或(||
):當且僅當左側運算爲假時纔對右側進行求值。
進行比較運算時除非比較的對象時布爾類型,不然不要使用布爾字面值true
if (val == true) {/*...*/} //只有當val等於1時條件才爲真
###4.4 賦值運算符
int k = 3.14159;
k = {3.14}; vector<int> vi = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
while ((i = get_value()) != 42){ //其餘處理 }
###4.5 遞增和遞減運算符
關於前置和後置++
運算符。 除非必須,不然不用遞增遞減運算符的後置版本。(由於對於複雜的迭代器類型,後置須要將原始值存儲下來,可能涉及到額外的消耗。)
混用解引用和遞增運算符。
auto pbeg = v.begin(); while (pbeg != v.end() && *pbeg >= 0) cout << *pbeg++ << endl;
後置++
的優先級高於解引用運算符,表示獲得初始值的副本再遞增。 這種*pbeg++
的寫法更簡潔。
###4.6 成員訪問運算符
表達式ptr->mem
等價於(*ptr).mem
。
解引用運算符*
優先級低於點運算符,因此執行解引用運算的子表達式兩端必須加上括號。(*p).size()
。
###4.7 條件運算符(?:
)
cond ? expr1 : expr2
,條件運算符只對expr1
和expr2
中的一個求值。expr1
和expr2
的類型應該相同或者能轉換成同一種類型。
條件運算符的優先級很是低,所以當一條長表達式中嵌套了條件運算子表達式時,一般須要在它兩端加上括號,尤爲是在輸出表達式中,不然將產生意想不到的效果。
cout << ((grade < 60) ? "fail" : "pass") << endl;
###4.8 位運算符
位運算符做用於整數類型的運算對象,並把運算對象當作是二進制位的集合。
若是運算對象是「小整型」,它的值會被自動提高成較大的整數類型。
移位運算符>> <<
右側的運算對象必定不能爲負,並且值必須嚴格小於結果的位數,不然將產生未定義的行爲。
利用位運算符能夠檢測位:
unsigned long quiz1 = 0;//32位 quiz1 |= 1 UL << 27;//表示將第27位置成1 quiz1 &= ~(1 UL << 27);//表示將第27位置成0 bool status = quiz1 & (1 UL << 27);//檢測第27位
###4.9 sizeof運算符
size_t
類型的常量表達式。當返回一個表達式的值時,sizeof expr
並不實際計算其運算對象的值。int i = 0; cout << sizeof( i++ ) << endl;//4 cout << i <<endl;//0
sizeof *p
:由於sizeof不會實際求運算對象的值,因此即便p是一個無效的(即未初始化)指針也不會有什麼影響。在sizeof
的運算對象中解引用一個無效指針仍然是一種安全的行爲,由於指針實際上並無被真正利用。
sizeof Sales_data::revenue
:C++11容許使用做用域運算符來獲取類成員的大小。sizeof
運算符無須咱們提供一個具體的對象就能訪問到類成員,由於要想知道類成員的大小無須真正的獲取該成員。
sizeof
運算符的結果: char
類型或類型爲char
的表達式 引用類型獲得被引用對象所佔空間的大小 指針類型獲得指針自己所佔空間大小 對數組執行獲得整個數組所佔空間大小,等價於全部元素各執行一次,並將結果求和。(而不會把數組當成指針處理) 對string
對象或vector
對象執行只返回類型固定部分的大小,不會計算對象中的元素佔用了多少空間。
vector<int> v{0}; cout << sizeof v << endl;//12
constexpr size_t sz = sizeof(ia) / sizeof(*ia); int arr2[sz];//正確,sizeof返回一個常量表達式。
###4.10 逗號運算符
int i = 1, j = 100; cout << (i++, j++) << endl;
###4.11 類型轉換
算術轉換:運算對象將轉換成最寬的類型;當表達式中既有浮點型也有整數類型時,整數值將轉換成浮點類型。
數組轉換成指針:在大多數用到數組的表達式中,數組自動轉換成轉向數組首元素的指針。當數組用做decltype
關鍵字參數,或者做爲取地址符(&
)、sizeof
及typeid
等運算符的運算對象時,上述轉換不會發生。
常量整數值0
或者字面值nullptr
能轉換成任意指針類型;指向任意很是量的指針能轉換成void*
;指向任意對象的指針能轉換成const void*
轉換成常量:能將指向T
的指針或引用分別轉換成指向const T
的指針或引用。但相反的轉換不存在。
int i; const int &j = i; const int *p = &i; int &r = j, *q = p;//錯誤,不容許const轉換成常量
string s, t = "a value";//字符串字面值轉換成string類型 while (cin >> s)//while的條件部分把cin轉換成bool值
強制類型轉換: cast-name<type>(expression)
:其中type
表示轉換的目標類型,expression
是要轉換的值。若是type
是引用類型,則結果是左值。cast-name
指定了執行的是那種轉換,包括static_cast
、dynamic_cast
、const_cast
和reinterpret_cast
中的一種。
static_cast
:具備明肯定義的類型轉換,只要不包含底層const
,均可以使用static_cast
。
double slope = static_cast<double>(j) / i;
當須要把一個較大的算術類型賦值給較小的類型時很是有用,此時意味着告訴編譯器不在意潛在的精度損失,而且不會產生警告。對於編譯器沒法自動執行的類型轉換也頗有用,例如能夠用來找回存在於void*
指針中的值:(但應當確保轉換類型必定正確)
void *p = &d;//任何很是量對象的地址都能存入void* double *dp = static_cast<double*>(p);
const_cast
:只能改變運算對象的底層const
(常量指針或聲明引用的const
),稱爲去掉const
行爲;它只能改變常量屬性,不能改變常量類型。const char *pc; char *q1 = const_cast<char*>(pc);//正確,可是經過p寫值是未定義行爲 char *q2 = static_cast<char*>(pc);//錯誤,static_cast不能轉換掉const性質 string s1 = const_cast<string>(pc);//錯誤,const_cast只轉換屬性 string s2 = static_cast<string>(pc);//正確,字符串字面值轉換爲string類型
const_cast
經常使用於有函數重載(6.4節)的上下文中。
const_cast
的用法不是將一個const變量變成非const
變量的。看一下標準就能夠知道,使用const_cast
把一個本來是const的變量轉換爲非const
,這是一個未定義行爲,是很是很是危險的舉動。const_cast
的目的,在於某些變量本來不是const
的,但因爲某種特殊緣由,無心間被變成了const
的,例如使用了一個const
引用指向了一個原本不是const
的對象。結果寫了一些代碼以後發現它實際上須要被修改。 這在平時的工做中不會遇到由於你能夠直接把const
引用修改爲非const
的,但C++
中可能的狀況太多,尤爲考慮到不少複用的時候,有時仍是會出現本不應是const
的對象被const
引用了這種狀況。尤爲是使用模板,比較複雜的狀況。這纔是const_cast的意義所在。(摘自:求助const_cast的問題(CSDN))
reinterpret_cast
:爲運算對象的位模式提供較低層次上的從新解釋。int *ip; char *pc = reinterpret_cast<char*>(ip);//pc所指的真實對象是一個int而非字符,若是把pc當成普通的字符指針使用就可能在運行時發生錯誤,例如string str(pc)
使用reinterpret_cast很是危險。使用一個int
的地址初始化pc
,顯示地聲稱這種轉換合法,編譯器不會報錯,但繼續使用將引起糟糕的後果。
dynamic_cast
支持運行時類型識別,19.2節介紹。
強烈建議避免使用強制類型轉換,除了重載函數上下文使用const_cast
(6.4節)無可厚非,其餘狀況都應反覆斟酌。
舊式強制類型轉換:
type (expr);//函數形式 (type) expr;//C語言風格
它們與上述三種強制轉換有類似行爲。