自制简单计算器
导言
好多次想学习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中添加一个转换,将输入的中缀表达式转换为后缀表达式后再进行计算。