C++ Primer 第五章

5.1 簡單語句

1.在一個表達式的末尾加上分號就是表達式語句,表達式與丟棄了表達式的求值結果。

1+2;

2.一個單獨的分號,是空語句,可以用佔位,或者某些語句語法上需要一個語句,但是實現時,已經在其他的部分完成了操作時使用。

;;;;;

3.複合語句,複合語句就是用花括號括起來的語句和聲明的序列。複合語句也叫做,一個塊爲一個作用域,在塊中定義的變量的作用域爲聲明這個變量開始的地方到塊結束

{}

注意塊的結尾沒有分號;如果你寫上了,表示就是多了一個空語句。

  1. 空塊相當於空語句

練習

1.空語句就是一個分號,或者一對空的花括號,前面沒有加任何的表達式。

當一個語句在語法上需要一個語句,但是在邏輯上已經該語句已經不需要任何操作時,可以用空語句

2.塊就是一對花括號括起來的一系列語句,用到塊的地方挺多的,其中一個例子就是:循環、條件等語句下,邏輯上需要執行多個操作時,可以使用塊語句。

while (val <= 10 && (sum += val,++val)) {

}

花裏胡哨,可讀性下降了不少

5.2 語句作用域

塊中定義的變量的訪問返回是從定義該變量開始的位置,到塊結束。

練習

a。

string::iterator iter;
while(iter!=s.end()){
//todo
}

b.

bool status;
while(status=find(word)){
//todo
}
if(!status){
//todo
}

5.2 條件語句

c++中有if和switch兩種條件語句

對於if條件語句,有if()和if() else兩種形式,()中的內容需要能夠轉化爲布爾類型

因爲存在懸垂else的問題,所以最好在if else的每個語句後面,都加上塊作用域,這樣提升了可讀性,能夠避免一些錯誤。

什麼是懸垂else,因爲if條件語句,可以寫else,也可以不寫,所以else和哪個if匹配就存在問題,C++遵循的是,和最近的那個沒被匹配的if語句進行匹配。

if(a==1)
	if (b==2)
		//to
else
	//todo

這裏的todo其實匹配的是後面的那個if,所以爲了避免懸垂else,在if,else語句後面都加上塊作用域,是一個非常好的選擇。

練習

a。少寫了分號
b。加上一個塊作用域
c。ival定義在了if中,
d。將=用成了==

switch

switch(…){case xx;}
switch語句需要在()中輸入整型表達式,同樣的case標籤也必須爲整型常量表達式,標籤不可以重複。
switch語句將()中的內容,與定義的多個case標籤進行匹配,從匹配的case標籤開始,向下執行所有語句,直到遇到break語句爲止。

int socre =1;
int a,b,c;
a=b=c=0
switch(score){
	case 1:
		++a;
	case 2:
		++b;
	case 3:
		++c;
		break
	default:
		;
	break;
}

上述的語句會從++a,++b,++c一直執行到break位置,一般情況下,每一個case標籤下都有一個break

default表示如果()中的內容和所有的case標籤都不匹配則執行該項。

case中可以定義變量,但是如果定義的變量的初始化和使用不在同一個case標籤的範圍下,則會報錯

int score = 1;
switch (score) {
case 1:
	int j = 0;
	cout << 1 << endl;
	break;
case 2:
	j = 1;
	cout << 2 << endl;
	break;
default:
	cout<<"default"<<endl;
	break;
}

該switch語句會報錯,因爲case 2直接使用了j,但是swithc語句可能會跳過case1直接執行case 2;爲了避免出現這種問題,一般在case 標籤中使用塊,在塊中定義變量,這樣提高了可讀性。

一個有趣的問題時:

switch (score) {
	cout<<"what"<<endl;

case 1:
	cout << 1 << endl;
	break;
case 2:
	//j = 1;
	cout << 2 << endl;
	break;
default:
	cout<<"default"<<endl;
	break;
}

上述代碼中,what並不會輸出,所以在這一塊定義的變量同樣不會被執行。一個可能的原因時,switch語句中,它只把()中的內容和case標籤進行比較,case標籤之外的語句操作都被忽略了。

練習

5.13

a。case標籤後面沒有break
b。default中使用了case1中定義的變量ix
c。要把case 1,3,5,7,9分開寫,case 1: case 3:.。。
d. case 標籤需要整型常量表達式

迭代語句

迭代語句又叫循環語句,分爲while和for。其中while語句又分爲

do{
statement
}while(condition);
while(condition){
	statement
}

其中do while至少會執行statement一次,而while則至少一次都不會執行。
注意do while是有分號結尾的。

當我們不知道需要循環多少次的時候,就是用while語句

在while語句的condition和statement裏面初始化的變量,每次循環都會被創建和銷燬,但是do while的condition內的語句必須是在循環體外創建的。

練習

太久沒有寫代碼,還花了挺長的時間寫的。。
錯誤的原因爲
錯誤的把=寫錯了==
以及邏輯上的bug

string str = "";
int temp_count = 1;
int max_count = 1;
string last_str = "";
string max_str = "";
while (cin>>str) {
	//第一次進入循環,需要將last_str賦值
	if (last_str=="") {
		last_str = str;
	}else if(last_str==str){//如果二者相等,則計數
		++temp_count;
		//last_str = str;
	}
	else if(last_str!=str){//不相等則判斷最大連續詞的次數
		if (max_count < temp_count) {
			max_str = last_str;
			max_count = temp_count;
		}
		temp_count = 1;
		last_str = str;
	}
}
if (max_count>0) {
	cout << max_str << "continue " << max_count<< endl;
}
}

需要注意的是,單詞最少都會出現一次。

傳統for語句

傳統的for的格式爲

for(1.init-statemetn;2.condition;4.expression){
	3.statement
}

1.ini-statement用來定義變量,可以定義多個變量,但是類型必須相同,這一點很好理解,因爲平時定義變量的時候,也不可以一條語句定義多個類型不同的變量。也可以不定義任何變量,但是分號要保留

int i,double j;//這樣定義是錯誤的
int i,j,k;//這樣是可以的

2.判斷是否執行循環體,如果爲空,則是死循環,除非有break語句跳出循環
3.循環體主體內容
4.一般情況下,是修改在1中定義的變量的狀態,但是也可以不寫

如果condition的條件第一次爲true,則傳統for的執行順序爲
1234 234 234 234 直到跳出循環爲止

練習

5.15

a。變量ix的定義在for循環體內,無法在作用域外訪問
b。for循環的初始化語句可以不寫,但是分號不能忽略
c。我覺得語法上沒有什麼錯誤,這裏的問題在於,如果ix一開始就不等於sz,則for循環會是一個死循環。

5.16
把上一個統計最大連續詞的用for寫了一遍

string str = "";
int temp_count = 1;
int max_count = 1;
string last_str = "";
string max_str = "";
for (;cin>>str;) {
	if (last_str =="") {
		last_str = str;
	}
	else if (last_str==str) {
		++temp_count;
	}
	else if (last_str!=str) {
		if (max_count<temp_count) {
			max_count = temp_count;
			max_str = last_str;
		}
		temp_count = 1;
	}
}

if (max_count>0) {
	cout << max_str << "continue " << max_count<< endl;
}

我更喜歡用for,因爲for中有init-statement步驟,我可以定義一些僅在for中有效的變量

5.17

vector<int> vec1 = { 0,1,2,3,3,2 };
	vector<int> vec2 = { 0,1,2 };
	auto iter1 = vec1.begin();
	auto iter2 = vec2.begin();
	bool is_prefix = true;
	while (iter2!=vec2.end()) {
		if (*iter2!=*iter1) {
			is_prefix = false;
		}
		++iter1;
		++iter2;
	}
	cout<<is_prefix<<endl;

//拓展一下,判斷vec2是否是vec1 的子序列

vector<int> vec1 = { 0,1,2,3,3,2 };
	vector<int> vec2 = { 2,3,3 };
	bool is_sequence = true;
	for (int i = 0; i < (vec1.size() - vec2.size());++i) {
		auto iter1 = vec1.begin()+i;
		auto iter2 = vec2.begin();
		bool is_sequence = true;
		while (iter2!=vec2.end()) {
			if (*iter1!=*iter2) {
				is_sequence = false;
				break;
			}
			++iter1;
			++iter2;
		}
		if (is_sequence==true) {
			cout<<"is sequence"<<endl;
			break;
		}
	}

範圍for

範圍for用來遍歷容器或者序列中的元素。 形式如下

for(declaration:expression){
statement
}

1.expression必須是一個能夠返回迭代器的序列,比如初始化列表,數組,string,vector。

2.declaration用於聲明變量,如果想要修改序列中的元素,必須聲明爲引用類型。一般使用auto自動推斷變量的類型,也可以自己寫,只要自己寫的類型可以由序列中的類型轉換即可

vector<int> vec = { 1,2,3,4,45,5 };
for (double i:vec) {
	cout<<i<<endl;
}

但是這是何苦呢?可讀性下降了。

5.4.4的練習

5.18

a.do下如果要執行多條語句,需要使用{}
b.在condition裏面定義變量,需要將變量定義在外面
c。在statement中定義變量,在condition裏面 使用,需要將變量定義在外面

5.19

string s1 = "";
string s2 = "";
do {
	
	cin >> s1 >> s2;
	string short_str = "";
	if (!s1.empty()||!s2.empty()) {
		if (s1.size()!=s2.size()) {
			 short_str = s1.size() > s2.size() ? s1 : s2;
		}
	}
	if (!short_str.empty()) {
		cout << short_str << endl;
	}
} while (!s1.empty()||!s2.empty());

5.5 跳轉語句

C++的中跳轉語句有4個,分別是break,continue,goto,return

1.break語句在while,do while,for這些循環體和switch中使用,用於結束距離其最近的循環或者switch,程序繼續執行循環體外的內容

2.continue,用在循環體內,用於結束當前這一次循環,程序會繼續檢查循環語句的condition,根據condition,判斷是否繼續循環

3.goto用於從函數的一處跳轉到另一處。

label:

goto label;

label: 用於標記要跳轉的地方,goto label;則是跳轉到要標記的地方。不推薦使用goto,因爲這讓程序變得可讀性差,隱藏隱患
4.return 用在函數內部,在當前函數下,return之後的語句都不會再執行。

Try語句和異常處理

異常處理這裏有幾個關鍵字,需要直到
throw
try{…}catch(expression){satement}

1.throw用於顯式的拋出一個異常

2.try{…}語句中,寫可能會發生錯誤的代碼,catch(){}用於捕獲異常,

3.catch的expression中寫的是異常的對象,如果try中發生了異常,則會將try的異常與這個try語句寫的catch中的異常一一對比,如果對比上了則,運行catch的statement,如果沒有對比上,則將異常拋出給它的調用者,如果一直沒有對應的catch捕獲這個異常,則這個異常交給terminate這個標準庫函數處理。

4.一個函數中如果發生異常,異常之後的語句將不再執行,而是尋找對應的catch,尋找到了對應的catch則執行catch下的語句,並繼續執行try catch語句後面的代碼。

try{
1/0;
1+2;//不會執行
throw std::runtime_error("error");
}catch(std::runtime_error err){
	//todo 執行
}
1+3//會執行

5.C++定義了一些標準異常類,他們在stdexcept裏面,使用時要使用std的命名空間,初次之外還有exception類型,bad_cast等異常類
在這裏插入圖片描述
在stdexcept中的異常類,除了exception初始化不需要傳入字符串,其餘的異常類都要傳入字符串進行初始化。

通過調用err.what()來輸出錯誤信息,what返回const char *類型的字符串,對於有初始值的異常類,what()返回的是初始化時的值,而沒有初始值的異常類what()返回的文本由系統決定。

練習

5.23
會報錯,系統會提示除數不能爲0
在這裏插入圖片描述
5.24

因爲沒有catch語句,所以提交了之後,由系統判斷情況,在vs2017中是,報錯,提示這個異常沒有被處理。
在這裏插入圖片描述

5.25

while (true) {
	int a=0, b=0;
	cin >> a>>b;
	try
	{
		if (b == 0) {
			throw std::runtime_error("numerator can not equal 0");
		}
		else {
			cout<<a/static_cast<double>(b)<<endl;
		}
	}
	catch (std::runtime_error error)
	{
		cout<<"contine input?\n n or y"<<endl;
		string temp;
		cin >> temp;
		if (temp=="n") {
			break;
		}
	}
}
發佈了46 篇原創文章 · 獲贊 6 · 訪問量 3114
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章