天天看點

《C++ Primer(第5版)》學習筆記(第5章)第5章 語句

第5章 語句

C++提供了條件執行語句、循環語句和用于中斷目前控制流的跳轉語句,本章将具體介紹這些語句。

5.1 簡單語句

空語句

最簡單的語句是空語句,隻有一個單獨的分号。如在程式的某個地方文法上需要一條語句但是邏輯上不需要,此時應該使用空語句。

// 讀取輸入流的内容直到遇到一個特定值
while (cin >> s && s != sought)
	; // 空語句,循環的全部工作在條件部分就可以完成
           

複合語句

用花括号括起來的語句和聲明的序列,也稱作塊,一個塊就是一個作用域,在塊中引入的名字隻能在塊内部以及嵌套在塊中的子塊裡通路。

5.2 語句作用域

可在if、switch、while和for語句的控制結構内定義變量,定義在控制結構中的變量隻在語句相應的塊内部可見,一旦語句結束,變量就超出其作用範圍了。

for (int i = 0; i <= n; ++i) // 在for語句内部定義i,隻能在for對應的塊中使用
	cout << i << endl;
i = 0;                       // 錯誤:在循環外部不能使用
           

5.3 條件語句

5.3.1 if 語句

if (condition1)  // condition1類型必須能轉換成布爾類型
	statement1; 
	// condition1為真:執行statement1,執行完後執行後面的其他語句,跳過else if和else語句
else if (condition2)
	statement2; 
	// condition1為假,condition2為真:跳過statement1,執行statement2,執行完後跳過else語句
else
	statement3; 
	// condition1和condition2為假:跳過statement1和statement2,執行statement3
           
當statement中有多條語句想要一起執行時,要使用花括号{}将其包覆。
當一個if語句嵌套在另一個if語句内部時,很可能if分支會多于else分支,C++規定else與離它最近的尚未比對的if比對。

5.3.2 switch 語句

switch (expression) { // 對表達式求值,然後與每個case标簽比較
	case val1:        // 比對成功則執行statement1,不成功則比對val2
		statement1;
		break;        // 遇到break後跳出switch語句
	case val2:
		statement2;
		break;
	case val3:
		statement3;
		break;
	default:          // 該語句可省略
		statement4;
		break;
// 若所有case标簽都未比對成功則執行緊跟在default标簽後的語句
}
           
如果某個case标簽比對成功,将從該标簽開始往後順序執行所有case、default分支(不管後面的case标簽有沒有比對上),除非中斷這一過程(如break)。

switch的執行過程可能會跨過某些case标簽,如果程式跳轉到了某個特定的case,則switch結構中該case标簽之前的部分會被忽略掉。如果在某處一個帶有初值的變量位于作用域之外,在另一處該變量位于作用域之内,則從前一處跳轉到後一處是非法行為。

5.4 疊代語句

5.4.1 while 語句

當不确定到底要疊代多少次,或者在循環結束後想通路循環控制變量的可以用while循環。

while (condition)
	statement
           

while語句的執行過程是交替地檢測condition和執行statement,直至condition為假為止。condition可以是一個表達式或者一個帶初始化的變量聲明。應該由條件本身或者是循環體設法改變condition的值,否則循環可能無法終止。

定義在while條件部分或者while循環體内的變量每次疊代都經曆從建立到銷毀的過程。

5.4.2 傳統的 for 語句

for (init-statement; condition; expression)
	statement
           

for循環的執行過程如下:

init-statement必須是以下三種形式中的一種:聲明語句、表達式語句或者空語句,可以定義多個對象,但是隻能有一條聲明語句,是以所有變量的基礎類型必須相同。

for 語句頭能省略掉init-statement、condition和expression中的任何一個(或者全部)

5.4.3 範圍 for 語句

for (declaration: expression)
	statement
           

expression表示的必須是一個序列,如用花括号括起來的初始值清單、數組或者vector或string等類型的對象,這些類型的共同特點是擁有能傳回疊代器的begin和end成員。

declaration定義一個變量,序列中每個元素都能轉換成該變量的類型。為確定類型相容,可使用auto類型說明符。如果需要對序列中的元素執行寫操作,循環變量必須聲明成引用類型。

每次疊代都會重新定義循環控制變量,并将其初始化成序列中的下一個值,之後才會執行statement。

vector<int> v = {0, 1, 2, 3, 4, 5};
// 範圍變量必須是引用類型,這樣才能執行寫操作
for (auto &r : v)
	r *= 2;
           

以上範圍for語句等價于:

for (auto beg = v. begin(), end = v.end(); beg != end; ++beg) {
	auto &r = *beg;
	r *= 2;
}
           

5.4.4 do while 語句

do
	statement
while (condition);
           

do while語句與while語句的差別是:do while語句先執行循環體statement,再檢查條件condition,如果condition為假,循環終止,否則,重複循環過程。

condition使用的變量必須定義在循環體之外。

5.5 跳轉語句

5.5.1 break 語句

break語句負責終止離它最近的while、do while、for或switch語句,并從這些語句之後的第一條語句開始繼續執行。break語句隻能出現在疊代語句或者switch語句内部(包括嵌套在此類循環裡的語句或塊的内部)。

5.5.2 continue 語句

continue語句負責終止離它最近的while、do while、for語句的目前疊代,并立即開始下一次疊代。continue語句隻能出現在疊代語句while、do while、for内部(包括嵌套在此類循環裡的語句或塊的内部)。

對while或do while語句:繼續判斷條件的值;對于傳統for循環:繼續執行for語句頭的expression;對于範圍for語句:用序列中的下一個元素初始化循環控制變量。

5.5.3 goto 語句

goto 語句的作用是從goto語句無條件跳轉到同一函數内的另一條語句。其形式為:

用辨別符+冒号的形式寫帶标簽的語句:

goto語句和控制權轉向的那條帶标簽的語句必須位于同一個函數内,goto語句不能将程式的控制權從變量的作用域之外轉移到作用域之内。

不要使用goto語句,因為它令程式很難了解且難修改。

5.6 try 語句塊和異常處理

5.6.1 throw 表達式

int x, y;
cin >> x >> y;
if (y != 0) {
	cout << x / y << endl;
	return 0;
}
else {
	cerr << "y must be not equal to zero" << endl;
	return -1;
}
           

上述程式計算兩個整數的除法運算,但在真實程式中,應把對象相除的代碼和使用者互動的代碼分離開來。是以改寫程式使得檢查完成後抛出一個異常:

if (y == 0)
	throw runtime_error("y must be not equal to zero")
cout << x / y << endl;
           

如果y等于0,就抛出一個異常,該異常是類型runtime_error的對象。抛出異常将終止目前的函數,并把控制權轉移給能處理該異常的代碼。

5.6.2 try 語句塊

try {
	program-statement 
	// 包括聲明在内的任意語句,在語句塊内部聲明的變量無法在外部通路
}
catch (expression-declaration) { 
	handler-statements
}
catch (expression-declaration) {
	handler-statements
} // ...
           

5.6.1中的代碼用try語句塊編寫:

while (cin >> x >> y) {
	try { // try語句塊是程式本來要執行的任務
		if (y == 0)
		throw runtime_error("y must be not equal to zero")
		cout << x / y << endl;
	}
	catch (runtime_error err) { // catch子句負責處理類型為runtime_error的異常
		cout << err.what() // what是runtime_error類的一個成員函數
			 << "\n Try Again? Enter y or n" << endl;
		char c;
		cin >> c;
		if (!cin || c == 'n')
			break;
	}
} 
           

在複雜系統中,程式遇到抛出異常代碼前,其執行路徑已經經過了多個try語句塊,如目前try語句塊被另一個try語句塊調用,而當異常被抛出時,首先搜尋抛出該異常的函數,若沒找到比對的catch子句,終止該函數,并在調用該函數的函數中繼續尋找,以此類推。如一段程式沒有try語句塊且發生了異常,系統會調用terminate函數并終止目前程式的執行。

5.6.3 标準異常

C++标準庫定義了一組類(分别在4個頭檔案中)用于報告标準庫函數遇到的問題:

  • exception:最通用的異常類exception,隻報告異常的發生,不提供任何額外的資訊。
  • stdexcept:幾種常用的異常類。包括之前提到的runtime_error等。
  • new:bad_alloc異常類型。
  • type_info:bad_cast異常類型。

标準庫異常類隻定義了幾種運算,包括建立或拷貝異常類型的對象,以及為異常類型的對象指派。隻能以預設初始化的方式初始化exception、bad_alloc和bad_cast對象,不允許為這些對象提供初始值。相反:應當使用string對象或者C風格字元串初始化這些類型的對象,但不允許使用預設初始化的方式。

異常類型隻定義了一個名為what的成員函數,該函數沒有任何參數,傳回值是一個指向C風格字元串的const char*,該字元串的目的是提供關于異常的一些文本資訊。

繼續閱讀