回顧第1章,第一章我們認(rèn)識(shí)到了變量的定義,定義ing時(shí)賦值,操作符重載(Overloaded),和沒(méi)有深入探討的構(gòu)造函數(shù),成員函數(shù)的概念、符號(hào)直接量(與字符直接量的區(qū)別),還有輸入輸出緩沖模型之其好處(三個(gè)事件才會(huì)刷新緩沖區(qū),輸出到設(shè)備上,分別是,緩沖區(qū)已經(jīng)滿,遇到cin,顯示要求刷新(如std::endl,控制符(manipulator)))。
這章我寫(xiě)得有點(diǎn)急切,應(yīng)為之前C++學(xué)過(guò),有些概念一跳而過(guò),看不懂的,可以往下找紅色字體處開(kāi)始(從循環(huán)不變式分析處開(kāi)始的分析,再回頭來(lái)看這個(gè))。
1 #include <iostream>
2 #include <string>
3
4 int main() {
5 //ask for the person's name
6 std::cout << "Please enter your first name: ";
7
8 //read the name
9 std::string name;
10 std::cin >> name;
11
12 //build the message that we intend to write
13 const std:;string greeting = "Hello, " + name + "!";
14
15 //we have to rewrite this part
16 }
#
#分析:我們現(xiàn)在需要重寫(xiě)(重構(gòu)//we have...后面的代碼),應(yīng)該這樣思考,以前的那個(gè)程序不具備好的可擴(kuò)展性,為什么呢?首先如果要求輸入的框架編程10行(空白#行變成10行),后面的代碼久要多加很多行,一行行的進(jìn)行輸出。這時(shí)我們可以用循環(huán)對(duì)代碼進(jìn)行重構(gòu)。我們先分析,在greeting上下空白行只有一行,所以我們用pad
#表示空白行,而總的行數(shù)為2 * pad + 3(頭尾加greeting那行)。這樣我們就可以讓程序輸出任意多行。于是有如下代碼
const int pad = 1;
const int rows = pad * 2 + 3;
#另外我們這個(gè)輸出的框架是要讓左右兩邊的空白數(shù)和上下兩端的空白數(shù)相同,所以也只需要定義一個(gè)變量就夠了。每一行輸出的字符數(shù)就是greeting的長(zhǎng)度加上pad * 2加上兩個(gè)#兩個(gè)星號(hào)。即如下代碼const std::string::size_type cols = greeting.size() + pad * 2 + 2;
1
2 #include <iostream>
3 #include <string>
4 using std::cin; using std::endl;
5 using std::cout; using std::string;
6 int main() {
7 cout << "Please enter your first name: ";
8
9 string name;
10 cin >> name;
11
12 const string greeting = "Hello, " + name + "!";
13
14 const int pad = 1;
15
16 const int rows = pad * 2 + 3;
17 const string::size_type cols = greeting.size() + pad * 2 + 2;
18
19 cout << endl;
20
21 // invariant:we have written r rows so far
22 for(int r = 0; r != rows; ++r) {
23
24 string::size_type c = 0;
25
26 // invariant:we have written c characters so far in the current row
27 while(c != cols) {
28
29 if(r == pad + 1 && c == pad + 1) {
30 cout << greeting;
31 c += greeting.size();
32 } else {
33
34 if(r == 0 || r == rows -1 || c == 0 || c == cols - 1)
35 cout << "*";
36 else
37 cout << " ";
38 ++c;
39 }
40 }
41
42 cout << endl;
43
44 }
45 return 0;
46 }
#第一個(gè)::說(shuō)明string名字定義在名字空間std中,而第二個(gè)::則表示size_type來(lái)自string類。std::string定義了size_type,用來(lái)表示一個(gè)string中含有的字#符數(shù)目。如果需要一個(gè)局部變量來(lái)表示一個(gè)string長(zhǎng)度,可以使用std::string::size_type類型定義一個(gè)變量。
#size_type是一個(gè)無(wú)符號(hào)的類型
#輸出邊界字符,如果r = 0,由循環(huán)不變式可以知道,現(xiàn)在一行也沒(méi)有輸出。所以當(dāng)r = row - 1,已經(jīng)輸出了row - 1行,接下來(lái)輸出的是最后一個(gè)部分,類似的,如果c = 0,輸出的將是第一列的部分。
#輸出邊界符號(hào):
#那么我們?nèi)绾闻袛噍敵鰃reeting這行呢,由循環(huán)不變式,我們可以 r = pad + 1 時(shí),c = pad + 1時(shí),開(kāi)始輸出greeting。
#第二章寫(xiě)得有點(diǎn)亂,上面代碼看不懂的,請(qǐng)看下面分析
#首先我們要介紹一個(gè)概念,叫做循環(huán)不變式,循環(huán)不變式就是我們?cè)O(shè)置一個(gè)斷言,讓該斷言在該循環(huán)中始終都成立,結(jié)束后也成立,這樣這個(gè)斷言其實(shí)就是這段程序的意思。看如
#下代碼:
//invariant:we have written r rows so far
int r = 0;
//setting r to 0 makes the invariant true
while(r != rows) {
//we can assume that the invariant is true here
//waiting a row of output makes the invariant false
std::cout << std::endl;
//incrementing r makes the invariant true again
r++;
}
//we can conclude that the invariant is true here
#首先你應(yīng)該想一想要確保不變式始終為true,只要確保在循環(huán)進(jìn)入點(diǎn)為true,一次循環(huán)結(jié)束點(diǎn)為true,那么這個(gè)不變式久永遠(yuǎn)為true,understand?如果還不理解,先吧我說(shuō)
#的這句話理解了,在繼續(xù)往下看,不然你不知道我在講什么東西!
#我們的不變式就是上述斷言
invariant:we have written r rows so far #我們分析過(guò),不變式的兩個(gè)斷點(diǎn),一個(gè)設(shè)在開(kāi)頭,一個(gè)在結(jié)尾,所以開(kāi)頭時(shí)r = 0。此時(shí)程序一行也沒(méi)輸出,不變式為true,在結(jié)尾處r++后,仍為true,為什么呢?舉個(gè)例子,r = 0,進(jìn)來(lái)之后,將輸出一行,所以此時(shí)r不應(yīng)該在為0,而應(yīng)該為1.
#這是每一行輸出的框架,轉(zhuǎn)換成for循環(huán)就是上面相應(yīng)的代碼,而至于另外一個(gè)循環(huán)一樣個(gè)道理。
#下面再介紹一個(gè)重要的概念,這個(gè)概念我之前還真沒(méi)學(xué)好,看完后,恍然大悟,大測(cè)大悟阿!那就是循環(huán)時(shí)的計(jì)數(shù)問(wèn)題。
#在C中C++中我們寫(xiě)循環(huán)經(jīng)常是重int i = 0,從0開(kāi)始是不?就算是,你是不是經(jīng)常這樣寫(xiě)for(int i = 0; i <= number; i++);但是更好的寫(xiě)法應(yīng)該是for(int i = 0; i #!= number; i++);為什么呢?請(qǐng)聽(tīng)我慢慢道來(lái).
#首先我們知道在不對(duì)稱區(qū)間[0, rows)計(jì)數(shù)的話,很明顯就是rows個(gè)數(shù),但是如果你使用的是對(duì)稱區(qū)間,[num,rows]則有rows - num + 1個(gè)數(shù),是不是很不明顯,再則從0開(kāi) #始一目了然,別說(shuō)你看不出來(lái),我在舉個(gè)例子(0,66],和[21,86]哪一個(gè)你能快速判斷出有幾個(gè)數(shù)。
#有的人又說(shuō),這算什么阿,我從1開(kāi)始貝[1,66],不就多算一個(gè)數(shù)么,習(xí)慣就好。我想說(shuō),你說(shuō)的沒(méi)粗,但我懶,用不對(duì)稱區(qū)間跟塊算出,更不會(huì)出錯(cuò)。在則,用不對(duì)稱區(qū)間的好 #處是容易和invariant(循環(huán)不變式)相結(jié)合,例如,如果你從1開(kāi)始計(jì)數(shù),有的人想我們把不變式改成現(xiàn)在輸出第r行,但是這樣是不能作為一個(gè)不變式的,所謂不變式,就是
#這個(gè)斷言永遠(yuǎn)正確,但是當(dāng)你結(jié)束循環(huán)時(shí)r = rows + 1,就變成了輸出第rows + 1行,但這個(gè)不變式就變成錯(cuò)的鳥(niǎo),understand。
#再則我們選者!=而不是<=來(lái)作為比較操作符。這個(gè)差別很小,但是很不一樣,前者,循環(huán)結(jié)束時(shí)(只要沒(méi)有在循環(huán)里break),就能判斷此時(shí)r = rows,但是如果是后者,我 #們這能證明至少輸出了rows行,為啥?回憶下學(xué)過(guò)的math,<=,是什么意思?
#還有一條好處,我就不羅嗦了,綜上所屬,你可以發(fā)現(xiàn)從0開(kāi)始計(jì)數(shù)的好處!,想當(dāng)一時(shí),在寫(xiě)鏈表時(shí),就是因?yàn)檫@個(gè)計(jì)數(shù)問(wèn)題,自己也整了個(gè)證明方法,哈哈,每想到早就有更 #簡(jiǎn)單的方式了。
#本人才疏學(xué)淺,看不懂的,可以留言討論之。