導言
好多次想學習C++,但都是望而卻步。這次因為專業方向原因(數字圖像處理),不得不走上學習C++的道路上。網上找了些C++的推薦書籍,入門的大多都是《C++程序設計語言》、《C++ primer》,《C++ primer plus》。這3本書都找來看,但是都沒有看完,都是看到類型部分就放棄了。網上推薦的看不了,就自個在圖書館尋尋覓覓,偶然找到了《C++ In Action 》,大致翻看了下,沒有用幾章介紹類型,就它了。新學期第一周,把第一部分語言的基礎看完了,這里就着書中計算器的例子做個總結。
簡單計算器
計算器基於堆棧,接受用戶數字和運算符號的輸入。輸入數字則保存在棧中,如果是運算符號則彈出棧中數字進行運算。計算器實現的是后綴表達式(posfix notation)的運算,不需要小括號輸入。
計算器的頂級對象當然是計算器本身,保存輸入的數字並進行運算。輸入的數字需要保存在棧中,並且每次運算完成后需要遍歷棧中元素,所以需要一個棧及棧的訪問器。另外用於的輸入獨立於計算器,這就需要一個輸入對象獲取用戶的輸入。
棧及其訪問器
棧的實現是基於數組的。其定義如下:
1: const int maxStack = 16 ;
2:
3: class LStack
4: {
5: //訪問器作為友元,可以訪問類的私有成員
6: friend class LStackSeq ;
7: public:
8: LStack()
9: :top(0) {}
10:
11: int Pop();
12: void Push(int i);
13: bool IsFull() ;
14: bool IeEmpty() ;
15: private:
16: int top ;
17: int arr[maxStack] ;
18: };
利用數組arr保存棧中元素,成員top作為指向棧頂的指針。其實現代碼還是很簡單的,這里就不貼出具體代碼了。
訪問器逐個返回棧中的元素,需要訪問棧的私有成員,作為棧的友元出現。其定義:
1: class LStackSeq
2: {
3: public:
4: LStackSeq(LStack const & stack) ;
5: bool AtEnd() const ; //是否完成
6: void Advance() ; //移動到下一項
7: int GetNum() const ;//當前項
8: private:
9: int iCur ;
10: LStack const & stack ;
11: };
iCur指向當前訪問的元素,Advance用來移動iCur實現元素遍歷,AtEnd判斷是否遍歷完了,GetNum取得當前元素的值。
實現了訪問器后,對棧中元素的遍歷就很方便了。
1: for(LStackSeq seq(stack) ; !seq.AtEnd() ; seq.Advance())
2: std::cout << seq.GetNum() << std::endl ;
輸入對象
從標准輸入cin讀取字符到緩沖器,並且根據輸入的第一個字符來判斷輸入的類別。輸入token可以分為三類:數字、運算符、非法字符。其定義如下:
1: const int maxBuf = 16;
2:
3: //3種token類型:數字、運算符、非法字符
4: const int number = 1 ;
5: const int error = 2 ;
6:
7: class Input
8: {
9: public:
10: Input();
11: //返回運算符
12: int Token() const {return token ;}
13: //將字符轉換為數字
14: int toNumber() const ;
15: private:
16: int token ;
17: char buffer[maxBuf] ;
18: };
構造函數Input根據用戶輸入判斷輸入字符的類別設置token的值。如果輸入是運算符,則token直接等於輸入字符,並可以通過Token()訪問;輸入是數字,則設置token值number,可以調用toNumber將該類輸入轉換為數字;還允許輸入負數,當輸入第一個字符是‘-’時,判斷輸入的第二個字符是否是數字。具體實現:
1: Input::Input()
2: {
3: std::cin >> buffer ;
4:
5: int c = buffer[0] ;
6: //根據輸入的第一個字符,來判斷token類型
7: if(std::isdigit(c))
8: token = number ;
9: else if (c == '+' || c == '*' || c == '/')
10: token = c ;
11: else if ( c == '-')
12: {
13: if(std::isdigit(buffer[1]))
14: token = number ;
15: else
16: token = c ;
17: }
18: else
19: token = error ;
20: }
21:
22: int Input::toNumber() const
23: {
24: assert(token == number) ;
25: return std::atoi(buffer) ;
26: }
計算器
從Input獲取用戶輸入,實現計算功能。如果得到的輸入類型是數字,則將其壓入到棧中;輸入是運算符,則彈出棧中元素進行運算。定義:
1: class Calculator
2: {
3: public:
4: bool Execute(Input const & input ) ;
5: LStack const & GetStack() {return stack ;}
6: private:
7: LStack stack ;
8: int calcu(int n1,int n2,int token) const ;
9: };
Execute從Input獲取用戶輸入,並決定是將輸入壓入到棧中,還是彈出棧中元素並調用calcu方法完成計算。
1: bool Calculator::Execute(Input const & input)
2: {
3: int token = input.Token();
4: bool status = false ;
5:
6: if(token == error)
7: {
8: std::cout << "Unknown token \n " ;
9: }
10: else if (token == number)
11: {
12: if(stack.IsFull())
13: {
14: std::cout << "Stack is full \n" ;
15: }
16: stack.Push(input.toNumber());
17: status = true ;
18: }
19: else
20: {
21: assert(token == '+' || token == '-' || token == '/' || token == '*') ;
22: if(stack.IeEmpty())
23: {
24: std::cout << "Stack is empty\n" ;
25: }
26: else
27: {
28: int num2 = stack.Pop();
29: int num1 ;
30: if(stack.IeEmpty())
31: num1 = num2 ;
32: else
33: num1 = stack.Pop() ;
34:
35: stack.Push(calcu(num1,num2,token));
36: status = true ;
37: }
38: }
39:
40: return status ;
41: }
42:
43: int Calculator::calcu(int n1,int n2,int token) const
44: {
45: int result ;
46: if(token == '+')
47: result = n1 + n2 ;
48: else if(token == '-')
49: result = n1 - n2 ;
50: else if( token = '*')
51: result = n1 * n2 ;
52: else if(token == '/')
53: {
54: if(n2 == 0)
55: {
56: std::cout << "Division by zero \n" ;
57: return 0 ;
58: }
59: else
60: {
61: result = n1 / n2 ;
62: }
63: }
64:
65: return result ;
66: }
測試代碼就不貼出了,其執行結果:
改進
計算器接受的輸入是后綴表達式,是很不方便的,可在Input中添加一個轉換,將輸入的中綴表達式轉換為后綴表達式后再進行計算。![]()

