第5章 語句
簡單語句(Simple Statements)
如果在程序的某個地方,語法上需要一條語句但是邏輯上不需要,則應該使用空語句(null statement)。空語句中只含有一個單獨的分號;
。
// read until we hit end-of-file or find an input equal to sought while (cin >> s && s != sought) ; // null statement
使用空語句時應該加上註釋,從而令讀這段代碼的人知道該語句是有意省略的。
多餘的空語句並非總是無害的。
// disaster: extra semicolon: loop body is this null statement while (iter != svec.end()) ; // the while body is the empty statement ++iter; // increment is not part of the loop
複合語句(compound statement)是指用花括號括起來的(可能爲空)語句和聲明的序列。複合語句也叫做塊(block),一個塊就是一個作用域。在塊中引入的名字只能在塊內部以及嵌套在塊中的子塊裏訪問。通常,名字在有限的區域內可見,該區域從名字定義處開始,到名字所在(最內層)塊的結尾處爲止。
語句塊不以分號作爲結束。
空塊的作用等價於空語句。
語句作用域(Statement Scope)
可以在if
、switch
、while
和for
語句的控制結構內定義變量,這些變量只在相應語句的內部可見,一旦語句結束,變量也就超出了其作用範圍。
while (int i = get_num()) // i is created and initialized on each iteration cout << i << endl; i = 0; // error: i is not accessible outside the loop
條件語句(Conditional Statements)
if語句(The if Statement)
if
語句的形式:
if (condition) statement
if-else
語句的形式:
if (condition) statement else statement2
其中condition是判斷條件,可以是一個表達式或者初始化了的變量聲明。condition必須用圓括號括起來。
- 如果condition爲真,則執行statement。執行完成後,程序繼續執行
if
語句後面的其他語句。 - 如果condition爲假,則跳過statement。對於簡單
if
語句來說,程序直接執行if
語句後面的其他語句;對於if-else
語句來說,程序先執行statement2,再執行if
語句後面的其他語句。
if
語句可以嵌套,其中else
與離它最近的尚未匹配的if
相匹配。
switch語句(The switch Statement)
switch
語句的形式:
switch
語句先對括號裏的表達式求值,值轉換成整數類型後再與每個case
標籤(case label)的值進行比較。如果表達式的值和某個case
標籤匹配,程序從該標籤之後的第一條語句開始執行,直到到達switch
的結尾或者遇到break
語句爲止。case
標籤必須是整型常量表達式。
通常情況下每個case
分支後都有break
語句。如果確實不應該出現break
語句,最好寫一段註釋說明程序的邏輯。
儘管switch
語句沒有強制要求在最後一個case
標籤後寫上break
,但爲了安全起見,最好添加break
。這樣即使以後增加了新的case
分支,也不用再在前面補充break
語句了。
switch
語句中可以添加一個default
標籤(default label),如果沒有任何一個case
標籤能匹配上switch
表達式的值,程序將執行default
標籤後的語句。
即使不準備在default
標籤下做任何操作,程序中也應該定義一個default
標籤。其目的在於告訴他人我們已經考慮到了默認情況,只是目前不需要實際操作。
不允許跨過變量的初始化語句直接跳轉到該變量作用域內的另一個位置。如果需要爲switch
的某個case
分支定義並初始化一個變量,則應該把變量定義在塊內。
case true: { // ok: declaration statement within a statement block string file_name = get_file_name(); // ... }
迭代語句(Iterative Statements)
迭代語句通常稱爲循環,它重複執行操作直到滿足某個條件才停止。while
和for
語句在執行循環體之前檢查條件,do-while
語句先執行循環體再檢查條件。
while語句(The while Statement)
while
語句的形式:
while (condition) statement
只要condition的求值結果爲true
,就一直執行statement(通常是一個塊)。condition不能爲空,如果condition第一次求值就是false
,statement一次都不會執行。
定義在while
條件部分或者循環體內的變量每次迭代都經歷從創建到銷燬的過程。
在不確定迭代次數,或者想在循環結束後訪問循環控制變量時,使用while
比較合適。
傳統的for語句(Traditional for Statement)
for
語句的形式:
for (initializer; condition; expression) statement
一般情況下,initializer負責初始化一個值,這個值會隨着循環的進行而改變。condition作爲循環控制的條件,只要condition的求值結果爲true
,就執行一次statement。執行後再由expression負責修改initializer初始化的變量,這個變量就是condition檢查的對象。如果condition第一次求值就是false
,statement一次都不會執行。initializer中也可以定義多個對象,但是只能有一條聲明語句,因此所有變量的基礎類型必須相同。
for
語句頭中定義的對象只在for
循環體內可見。
範圍for語句(Range for Statement)
範圍for
語句的形式:
for (declaration : expression) statement
其中expression表示一個序列,擁有能返回迭代器的begin
和end
成員。declaration定義一個變量,序列中的每個元素都應該能轉換成該變量的類型(可以使用auto
)。如果需要對序列中的元素執行寫操作,循環變量必須聲明成引用類型。每次迭代都會重新定義循環控制變量,並將其初始化爲序列中的下一個值,之後纔會執行statement。
do-while語句(The do-while Statement)
do-while
語句的形式:
do statement while (condition);
計算condition的值之前會先執行一次statement,condition不能爲空。如果condition的值爲false
,循環終止,否則重複執行statement。
因爲do-while
語句先執行語句或塊,再判斷條件,所以不允許在條件部分定義變量。
跳轉語句(Jump Statements)
跳轉語句中斷當前的執行過程。
break語句(The break Statement)
break
語句只能出現在迭代語句或者switch
語句的內部,負責終止離它最近的while
、do-while
、for
或者switch
語句,並從這些語句之後的第一條語句開始執行。
string buf; while (cin >> buf && !buf.empty()) { switch(buf[0]) { case '-': // process up to the first blank for (auto it = buf.begin()+1; it != buf.end(); ++it) { if (*it == ' ') break; // #1, leaves the for loop // . . . } // break #1 transfers control here // remaining '-' processing: break; // #2, leaves the switch statement case '+': // . . . } // end switch // end of switch: break #2 transfers control here } // end while
continue語句(The continue Statement)
continue
語句只能出現在迭代語句的內部,負責終止離它最近的循環的當前一次迭代並立即開始下一次迭代。和break
語句不同的是,只有當switch
語句嵌套在迭代語句內部時,才能在switch
中使用continue
。
continue
語句中斷當前迭代後,具體操作視迭代語句類型而定:
- 對於
while
和do-while
語句來說,繼續判斷條件的值。 - 對於傳統的
for
語句來說,繼續執行for
語句頭中的第三部分,之後判斷條件的值。 - 對於範圍
for
語句來說,是用序列中的下一個元素初始化循環變量。
goto語句(The goto Statement)
goto
語句(labeled statement)是一種特殊的語句,在它之前有一個標識符和一個冒號。
end: return; // labeled statement; may be the target of a goto
標籤標識符獨立於變量和其他標識符的名字,它們之間不會相互干擾。
goto
語句的形式:
goto label;
goto
語句使程序無條件跳轉到標籤爲label的語句處執行,但兩者必須位於同一個函數內,同時goto
語句也不能將程序的控制權從變量的作用域之外轉移到作用域之內。
建議不要在程序中使用goto
語句,它使得程序既難理解又難修改。
try語句塊和異常處理(try Blocks and Exception Handling)
異常(exception)是指程序運行時的反常行爲,這些行爲超出了函數正常功能的範圍。當程序的某一部分檢測到一個它無法處理的問題時,需要使用異常處理(exception handling)。
異常處理機制包括throw
表達式(throw expression)、try
語句塊(try block)和異常類(exception class)。
- 異常檢測部分使用
throw
表達式表示它遇到了無法處理的問題(throw
引發了異常)。 - 異常處理部分使用
try
語句塊處理異常。try
語句塊以關鍵字try
開始,並以一個或多個catch
子句(catch clause)結束。try
語句塊中代碼拋出的異常通常會被某個catch
子句處理,catch
子句也被稱作異常處理代碼(exception handler)。 - 異常類用於在
throw
表達式和相關的catch
子句之間傳遞異常的具體信息。
throw表達式(A throw Expression)
throw
表達式包含關鍵字throw
和緊隨其後的一個表達式,其中表達式的類型就是拋出的異常類型。
try語句塊(The try Block)
try
語句塊的通用形式:
try { program-statements } catch (exception-declaration) { handler-statements } catch (exception-declaration) { handler-statements } // . . .
try
語句塊中的program-statements組成程序的正常邏輯,其內部聲明的變量在塊外無法訪問,即使在catch
子句中也不行。catch
子句包含關鍵字catch
、括號內一個對象的聲明(異常聲明,exception declaration)和一個塊。當選中了某個catch
子句處理異常後,執行與之對應的塊。catch
一旦完成,程序會跳過剩餘的所有catch
子句,繼續執行後面的語句。
如果最終沒能找到與異常相匹配的catch
子句,程序會執行名爲terminate
的標準庫函數。該函數的行爲與系統有關,一般情況下,執行該函數將導致程序非正常退出。類似的,如果一段程序沒有try
語句塊且發生了異常,系統也會調用terminate
函數並終止當前程序的執行。
標準異常(Standard Exceptions)
異常類分別定義在4個頭文件中:
-
頭文件exception定義了最通用的異常類
exception
。它只報告異常的發生,不提供任何額外信息。 -
頭文件stdexcept定義了幾種常用的異常類。
-
頭文件new定義了
bad_alloc
異常類。 -
頭文件type_info定義了
bad_cast
異常類。
標準庫異常類的繼承體系:
只能以默認初始化的方式初始化exception
、bad_alloc
和bad_cast
對象,不允許爲這些對象提供初始值。其他異常類的對象在初始化時必須提供一個string
或一個C風格字符串,通常表示異常信息。what
成員函數可以返回該字符串的string
副本。