自制簡單計算器


導言

好多次想學習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中添加一個轉換,將輸入的中綴表達式轉換為后綴表達式后再進行計算。大聲笑


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM