自制简单计算器

导言

好多次想学习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:  }

测试代码就不贴出了,其执行结果:

image

改进

计算器接受的输入是后缀表达式,是很不方便的,可在Input中添加一个转换,将输入的中缀表达式转换为后缀表达式后再进行计算。大声笑

posted @ 2014-02-23 16:40  Brook_icv  阅读(4284)  评论(0编辑  收藏  举报