前言 Stanley B.Lippman 先生所著的《C++ Primer》是學習C++的一本非常優秀的教科書,但《C++ Primer》作為一本大部頭書,顯然不適合所有的初學者。所以Lippman先生又返璞歸真地寫了這本短小輕薄的《Essentia C++》。這本書以簡短的章節篇幅,幫助初學者快速... ...
前言
Stanley B.Lippman 先生所著的《C++ Primer》是學習C++的一本非常優秀的教科書,但《C++ Primer》作為一本大部頭書,顯然不適合所有的初學者。所以Lippman先生又返璞歸真地寫了這本短小輕薄的《Essentia C++》。這本書以簡短的章節篇幅,幫助初學者快速學習C++的語法,瞭解C++語言特性,理解C++的設計目的和基本原理。筆者閱讀的是《Essential C++》中文版,其譯者名為侯捷,他也是《C++ Primer》中文版第三版的譯者。
基礎知識
第一個完整的C++程式:
#include <iostream> #include <string> using namespace std; int main() { string user_name; cout << "Please enter your first name:"; cin >> user_name; cout << '\n' << "Hello, " << user_name << "...and goodbye!\n"; return 0; }
關鍵字(keyword),就是程式語言預先定義的一些具有特殊意義的名稱。
函數(function)是一塊獨立的程式代碼序列,能夠執行一些運算。它包含四個部分,返回值類型(return type),函數名稱,參數列表(parameter list),以及函數體(function body)。main並非是程式語言定義的關鍵字,但是,C++編譯系統會假設程式中定義有main()函數。main()函數是程式執行的起點,如果我們沒有定義,程式將無法執行。
類(class),class機制賦予了我們“增加程式內之類型抽象化層次”的能力。class機制讓我們得以將數據類型加入我們的程式中,並有能力識別它們。面向對象的類層次體系(class hierarchy)定義了整個家族體系的各個相關類型。class的定義一般來說分為兩部分,分別寫在不同的文件中。一個就是所謂的“頭文件(header file)”,用來聲明該class所提供的的各種操作行為(operation)。另一個文件,程式代碼文件(program text),則包含了這些行為的實現內容(implementation)。欲使用class,我們必須先在程式中包含其頭文件,頭文件可以讓程式知道class的定義。
命名空間(namespace),是一種將庫名稱封裝起來的方法。通過這種方法,可以避免和應用程式發生命名衝突得到問題(所謂命名衝突是指在應用程式內兩個不同的實體(entity)具有相同名稱),導致程式無法區分兩者。命名衝突發生時,程式必須等到該命名衝突獲得解析(resolve)之後,才得以繼續執行。命名空間像是在眾多名稱的可見圍內豎起一道圍牆。
為了定義對象,我們必須為它命名,並賦予它數據類型。對象名稱可以是任何字母、數字、下畫線的組合,並大小寫敏感。對象名稱不能以數字開頭。當然任何命名不能和程式語言本身關鍵字完全一致。例如 delete 是語言關鍵字,所以 string class 採用 earse() 而非 delete() 來表示“刪去一個字元”的原因。
template class 允許我們在“不必指明 data member 類型”的情況下定義class。template class 機制使程式員得以直到使用 template class 時才決定真正的數據類型。程式員可以先插入一個代名,稍後才綁定至實際的數據類型。
由於反斜線字元以用作轉義字元的起頭字元,因此連續兩個反斜線即表示一個真正的反斜線字元。
被定義為 const 的對象,在獲得初值之後,無法在有任何變動。如果你企圖為 const 對象指定新值,會產生編譯錯誤。
對於 OR 邏輯運算符( || ),左側表達式會先被求值,如果其值為 true,剩下的表達式就不需要再被求值(此所謂短路求值法)。AND 邏輯運算符( && ),最左側表達式會先被求值,其結果若為 false ,則 AND 運算符的求值結果即為 false,其餘表達式不會再被求值。
一些運算符優先順序簡列於下,位置在上者的優先順序高於位置在下者,同一行的各種運算符具有相同的優先順序,其求值次序取決於它在該表達式中的位置(由左至右)。
邏輯運算符 NOT
算術運算符(*,/,%)
算術運算符(+,-)
關係運算符(<,>,<=,>=)
關係運算符(==,!=)
邏輯運算符 AND
邏輯運算符 OR
賦值運算符 (assignment = )
如果要訪問一個由指針所指的對象,我們必須對該指針進行提領(dereference,也叫解引用)操作——也就是取得“位於該指針所指記憶體地址上”的對象。在指針之前使用“*”號,便可以達到這個目的。
我們可以使用 dot 成員選擇運算符(member selection opereation),用來選擇我們想要的操作。如果要通過指針來選擇操作,必須改用 arrow 成員選擇運算符。如果要使用下標運算符(subscript operator),我們必須先提領指針,由於下標運算符的優先順序較高,因此指針提領操作的兩旁必須加上小括弧。
練習題答案
練習1.5 編寫一個程式,能夠詢問用戶的姓名,並讀取用戶所輸入的內容。請確保用戶輸入的名稱長度大於兩個字元。如果用戶的確輸入了有效的名稱,就響應一些信息。請以兩種方式實現:第一種使用C-style字元串,第二種使用string對象。
#include <iostream> #include <string> #include <iomanip> #include <cstring> using namespace std; #define MAX 50 #define MIN 2 int main() { //C-style 字元串 /* const int nm_size = 128; //分配一個固定大小 char user_name[nm_size]; cout << "Please enter your name:"; cin >> setw(nm_size) >> user_name; //保證讀入不超過127個字元,最後一個空間保存null size_t len = strlen(user_name); if (len <= 3) { cout << "Please enter a longer name!" << endl; return 0; } cout << "Hello," << user_name << endl; */ //string對象 string user_name; cout << "Please enter your name:"; cin >> user_name; size_t len = user_name.size(); //獲取的即為字元串長度,無null if (len <= 2) { cout << "Please enter a longer name!" << endl; return 0; } cout << "Hello," << user_name << endl; return 0; }
練習1.6 編寫一個程式,從標準輸入設備讀取一串整數,並將讀入的整數依次放到 array 和 vector,然後遍歷這兩種容器,求取值總和。將總和及平均值輸出至標準輸出設備。
#include <iostream> #include <vector> using namespace std; #define MAX 10 int main() { /* //存放到vector vector<double> v; double temp=0; double sum = 0; double ave = 0.0; while (cin >> temp) { v.push_back(temp); } for (int i = 0;i < v.size();i++) { sum += v[i]; } ave = sum / v.size(); cout << "Sum=" << sum << endl; cout << "Average=" << ave << endl; */ //存放到array double arr[MAX]; int count = 0; int count2 = 0; double temp; double sum = 0.0; double ave = 0.0; while (count<10 && cin >> temp) { arr[count] = temp; count++; } count2 = count; count--; while (count >= 0) { sum += arr[count]; count--; } ave = sum / count2; cout << "Sum=" << sum << endl; cout << "Average=" << ave << endl; return 0; }
練習1.7 使用你最稱手的編輯工具,輸入兩行(或更多)文字存檔。然後編寫一個程式,打開該文本文件,將其中的每個字都讀取到一個 vector<string> 對象中。遍歷該 vector,將內容顯示到 cout。然後利用泛型演算法 sort(),對所有文字排序。
#include <iostream> #include <fstream> #include <algorithm> #include <string> #include <vector> using namespace std; int main() { ifstream in_file("1.txt"); if (!in_file) { cerr << "opps! unable to open input file\n"; return -1; } ofstream out_file("2.txt"); if (!out_file) { cerr << "opps! unable to open output file\n"; return -1; } string word; vector<string> text; while (in_file >> word) text.push_back(word); size_t ix; cout << "unsorted text:\n"; for (ix = 0;ix < text.size();++ix) { cout << text[ix] << ' '; } cout << endl; sort(text.begin(), text.end()); cout << "sorted text:\n"; for (ix = 0;ix < text.size();++ix) { cout << text[ix] << ' '; out_file << text[ix] << ' '; } cout << endl; out_file << endl; return 0; }
練習1.8 1.4節的 switch 語句讓我們得以根據用戶答錯的次數提供不同的安慰語句。請以 array 儲存四種不同的字元串信息,並以用戶答錯的次數作為 array 的索引值,以此方式來顯示安慰語句。
#include <iostream> #include <stdlib.h> #include <ctime> using namespace std; const char* msg_to_usr(int num_tries); int main() { int count=0; srand(time(0)); int answer = rand() % 3; int myAnswer; char isContinue = 'y'; while (isContinue=='y') { cout << "Please input your answer(0-2): "; cin >> myAnswer; if (myAnswer == answer) { cout << "Congratulations!" << endl; break; } else { count++; cout << msg_to_usr(count) << endl; cout << "Continue? input y or n: "; cin >> isContinue; } } return 0; } const char* msg_to_usr(int num_tries) { const int rsp_cnt = 5; static const char* usr_msgs[rsp_cnt] = { "Go on, make a guess. ", "Oops! Nice guess but not quite it. ", "Hmm. Sorry. Wrong a second time. ", "Ah, this is harder than it looks, no? ", "It must be getting pretty frustrating by now! " }; if (num_tries < 0) { num_tries = 0; } else if (num_tries >= rsp_cnt) num_tries = rsp_cnt - 1; return usr_msgs[num_tries]; }
end。
“取乎其上,得乎其中;取乎其中,得乎其下;取乎其下,則無所得矣。”