第4章 表達式

第4章 表達式

昨天寫博客時用的是博客園自帶的 MarkDown編輯器,一點兒都很差用,插入代碼塊和段落縮進很難搞,傳統的 MarkDown語法說四個空格或者一個 Tab就能夠縮進,結果博客園自帶的編輯器裏這樣作竟然不行。再搜了不少個 MarkDown編輯器後,決定仍是使用小書匠,由於這個好像能夠直接把文章發佈到博客園上。這就很方便了。話很少說,進入正題。
1.運算符的三個關鍵點:優先級、結合律、求值順序。
2.在重載運算符時,運算對象的類型和返回值的類型能夠改變,但運算對象的個數、運算符的優先級和結合律都是沒法改變的。
3.decltype做用於表達式時,當表達式的求值結果是左值時,獲得的是引用類型;當求值結果是右值時,獲得的是類型。html

int a = 5;
	int *p = &a;
	decltype(*p) p1;  // p1是 int &
	decltype(&p) p2;  // p2是 int **

4.C++語言對於大多數的運算符並無規定求值順序,對於這些運算符,若是表達式指向並修改了同一個對象,將會引起錯誤併產生未定義的行爲。數組

int i = 0;
	cout << i << " " << ++i << endl;    // 未定義

編譯器可能先求 ++i的值再求 i的值,也可能先求 i的值再求 i的值。此表達式的行爲不可預知。有 4種運算符規定了它們的求值順序,分別是 &&、||、條件(?:)和逗號(,)。
5.對於整數的除法和取餘運算中,C11新標準中規定商一概向 0整除(即直接切除小數部分)。安全

21 % -5;       /* 結果是 1 */       21 / -5;       /* 結果是 -4 */ 
	-21 % -5;      /* 結果是 -1 */    -21 / -5;       /* 結果是 4 */

6.對於二元運算符,算術>關係>邏輯>賦值。因此在條件語句中,賦值部分一般加上括號。
if ((i = get_value()) != 42)
7.對於遞增/遞減運算符,優先使用前置版本,由於後置版本須要在修改前將原始值存儲下來,效率更低。
8.條件運算符的優先級很是低,在輸出表達式中使用條件運算符時要在兩端加上括號。編輯器

cout << ((grade < 60) ? "fail" : "pass");    // 輸出 pass或 fail
	cout << (grade < 60) ? "fail" : "pass";      // 輸出 1或者 0後根據 cout的值輸出 pass或 false

9.若是運算對象是帶符號的且它的值爲負,那麼位運算符如何處理運算對象的「符號位」依賴於機器。並且,此時的左移操做可能會改變符號位的值,是一種未定義行爲。所以建議僅將位運算符用於處理無符號類型。
10.位異或運算符(^),兩個運算對象相同,結果爲 0,反之爲 1。真值表以下ide

  0 1
0 0 1
1 1 0

11.sizeof,並不實際計算其運算對象的值。所以,在做用於解引用的指針時,即便該指針是一個未初始化的指針也不會有影響,它返回的是所值類型的空間大小。
  對 char或者類型爲 char的表達式執行 sizeof運算,結果得 1。
  對引用類型執行 sizeof運算,獲得被引用對象所佔空間的大小。
  對指針執行 sizeof運算,獲得指針自己所佔空間的大小。
  對解引用指針執行 sizeof運算,獲得指針所指的對象所佔空間的大小,指針不需有效。
  對數組執行 sizeof運算獲得整個數組所佔空間的大小,並不會將數組轉換爲指針。
  對 string或 vector對象執行 sizeof運算,只返回該類型固定部分的大小,和裏面存放了多少數據無關。
12.對無符號類型和帶符號類型進行運算,其結果比較複雜,也依賴於具體機器,因此應該儘可能避免無符號類型和帶符號類型的運算!
13.類型轉換函數

  1. 隱式類型轉換
    1. 類型提高 好比 bool、char、signed char、unsigned char、short和 unsigned short可能會被提高爲 int或 unsigned int類型。
    2. 算術類型轉換,將運算對象轉換成最寬的類型。好比表達式中既有浮點型也有整型數據時,整數會轉換成相應的浮點型。
    3. 數組轉換成指針。大多數狀況下,數組都能轉換成指向首元素的指針。可是當數組做爲 decltype的參數、取地址符(&)、sizeof和 typeid的運算對象時,數組並不會轉換爲指針。而在進行模板實參推斷時,若是函數形參不是引用類型,則能夠將數組或函數類型轉換爲普通的指針;相反則不能夠。
    4. 指針的轉換。
        1. 常量整數值 0或字面值 nullptr能轉換成任意指針類型。
        2. 指向任意很是量的指針能轉換成 void*。
        3. 指向任意對象的指針能轉換成 const void*。
        4. 派生類指針或引用能轉換成基類的指針或引用。
    5. 算術或指針類型向布爾類型的轉換。
    6. 指向很是量的指針或引用轉換成相應的常量類型的指針或引用。
    7. 類類型定義的轉換。對於未使用 explicit修飾的構造函數或重載了類型轉換運算符的類類型,編譯器能夠自動執行一次類類型的轉換。
  2. 顯式類型轉換
    1. static_cast。任何具備明肯定義的類型轉換,只要不包括底層 const,均可以使用 static_cast。當須要將較大的算術類型賦值給較小的算術類型時,static_cast很是有用,它能夠關閉編譯器給出的警告信息。另外還能夠找回存在於 void*指針中的值。
      double d = 3.14;
      int i = static_cast<int>(d);
      void *p = &d;
      double *pd = static_cast<double*>(p); // 將 void*轉換回初始的指針類型
    2. const_cast。只能改變運算對象的底層 const,將常量對象轉換成很是量對象。
    3. reinterpret_cast。對運算對象提供較低層次上的從新解釋,使用起來很是危險,通常不建議使用。
    4. dynamic_cast。運行時類型識別,用於將基類指針或引用安全地轉換爲派生類的指針或引用。 14.舊式的類型轉換從表現形式上不如強制轉換那麼明顯,一旦出現問題,追蹤起來並不容易。因此新的 C++程序,推薦使用顯式類型轉換。
相關文章
相關標籤/搜索