C++的那些事:表示式與語句

☆Ronny丶發表於2014-04-11

表示式

1,應該把函式呼叫當作是一種運算子,這種運算子對參與運算的物件沒有數量限制。

2,關於“左值(lvalue)”和“右值(rvalue)”可以做一個簡單的歸納:當一個物件被用作右值的時候,用的是物件的值(內容);當物件被用作左值的時候,用的是物件的身份(在記憶體中的位置)。

更具體的左值與右值的舉例:C++ priemr 5ed P121

3,運算子的優先順序規定了運算物件的組合方式,但是沒有說明運算物件按照什麼順序求值,在大多數的情況下,不會明確求值的順序,比如對於下面的表示式:

int i= f1() + f2();

先呼叫f1還是先呼叫f2是未知的。因此,對於這種運算子來說避免多個運算物件共同修改同一個變數,如下面的程式碼中,最終輸出是未能確定的:

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

但C++中有4種運算子是明確規定了運算物件的求值順序的:邏輯與“&&”、邏輯或"||"、條件(?:)運算子、逗號運算子","。這也是為什麼在我們自己定義的類型別中,一般不會去過載這幾種操作符。

4,短路求值:邏輯與運算子和邏輯或運算子都是先求左側運算物件的值再求右側運算物件的值,當且僅當左側運算物件無法確定表示式的結果時才會計算右側運算物件的值。

5,區別i++與++i

後置操作符需要先儲存原來的值,再將i+1,然後返回原來的值的副本;而前置操作符,只需要在原來值上加1,然後返回。所以++i比i++效率更高,當然如果i為int型別或指標時,編譯器會對i++進行優化,但如果是其他類型別或複雜類的迭代器時就不會了。

6,注意解除引用操作符與++操作符的優先順序,在實際程式碼中為了簡潔經常將*(i++)寫為*i++。因為++的優先順序高於解除引用操作符。

7,在使用條件操作符時,儘量避免寫出深度巢狀的條件操作符。另外條件操作符的優先順序非常低,在表示式中使用時要注意加括號,比如:cout<<(i<j?i:j);

8,關於sizeof運算子。sizeof的運算結果是編譯時的常量,注意下面的程式碼的值:

1 int a[10];
2 int* p = a;
3 int n1 = sizeof(a) / sizeof(*a); // n1=10
4 int n2 = sizeof(p) / sizeof(*p); // n2=1

sizeof運算子小貼士:

1 對char或者型別為char的表示式執行sizeof運算,結果為1。
2 對引用型別執行sizeof運算得到被引用物件年佔空間的大小。
3 對指標執行sizeof運算得到指標本身所佔空間的大小。
4 對解引用指標執行sizeof運算得到的指標指向的物件所佔空間的大小,指標不需要有效。
5 對陣列執行sizeof運算得到的整個陣列所佔空間的大小,等價於對陣列中所有的元素各執行一次sizeof運算並將所得結果求和。注意sizeof運算不會把陣列轉換成指標來處理。
6 對sting物件或vector物件執行sizeof運算只返回該型別固定部分的大小,不會計算物件中的元素佔用了多少空間。

9,在複合表示式求值時,要特別注意運算子的優先順序與結合性。特別地,!=與==的優先順序小於<=,>=等關係運算子。

10,型別轉換

1)隱式轉換

下面情況下,編譯器會自動地轉換運算物件的型別:

1 在大多數表示式中,比int型別小的整數值首先提升為較大的整數型別。
2 在條件中,非布林值轉換成布林型別。
3 初始化過程中,初始值轉換成變數的型別;在賦值語句中,右側運算物件轉換成左側運算物件的型別。
4 如果算術運算或關係運算的運算物件有多種型別,需要轉換成同一種型別。
5 函式呼叫時,實參與形參之間的型別轉換。
6 類型別可以定義一些轉換函式。

2)顯式轉換

1 statci_cast<Type>:任何具有明確定義的型別轉換,只要不包含底層const
2 const_cast<Type>:只能改變運算物件底層const
3 reinterpret_cast<Type>:通常為運算物件的位模式提供較底層次上的重新解釋。
4 dynamic_cast<Type>:執行時型別識別。

語句

1,和大多數語言一樣,C++提供了條件執行語句、重複執行相同程式碼的迴圈語句和用於中斷前前控制流的跳轉語句。

2,在使用塊狀語句時注意,在塊狀語句內定義的變數作用域只在塊狀區域內。特別地,在控制語句,比如if或for語句中,初始化或定義的變數,都只有塊區域的作用域。

3,switch語句的使用。case標號必須是整形常量表示式,不允許在switch語句內定義變數如果在它下面還有case或default語句,因為這樣會在某些情況下,在沒有執行變數定義的case分支的情況下,執行變數定義下面case分支。除非把變數定義在程式碼塊內。

4,在for迴圈中,如果有continue語句,會跳下continue後面的語句,但是不會跳變for語句中的計數器變化語句。

5,範圍for語句

範圍for語句是C++11新引入的內容,這種語句可以遍歷容器或其他序列的所有元素。它的語法形式為:

1 for(declaration:expression)
2   statement;

其中expression表示的必須是一個序列,比如用花括號括起來的初始值列表或者vector或string等型別物件。這些型別的共同特點是擁有能返回迭代器的begin和end成員。

declaration定義一個變數,序列中的每個元素都得能轉換成該變數的型別。確保型別相容最簡單的辦法是使用auto型別說明符,這個關鍵字可以令編譯器幫助我們指定合適的型別。如果需要對序列中的元素執行寫操作,迴圈變數必須宣告成引用型別。

下面是用範圍for語句來遍歷一個vector的例子。

1 vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
2 for (auto &r : v)
3 {
4     r *= 2;
5 }

在使用範圍for時語句時,不能通過範圍for語句增加vector物件的元素。因為在範圍for語句中,預存了end()的值。一旦在序列中新增(刪除)元素,end函式的值就可能變得無效了。

相關文章