MFC计算器 用vector实现和计算逆波兰表达式

         短学期本来已逃脱计算器的厄运,因为一直纠结不清我计算器的架构,不知如何下手;但学了MFC的底层实现机制和框架后发现计算器还是比较好实现的,于是总结下做计算器的架构和关键算法

      先声明:数据类型是CString,并用vector进行操作,算法为逆波兰

      我用MFC做的计算器架构比较简单,界面上除括号外的所有按钮都是直接在编辑框中显示,成为一个算术表达式,“=”作为一个响应函数,建立CMyCalculator的实例,再把算术表达式作为参数调用计算函数,得到结果更新显示在界面上。

void CTestDlg::OnBUTTONEqual()       //"="的响应函数
{
        CMyCalculator myCalculator(m_sTest);    
        if(myCalculator.IsExpression())     //检查式子的有效性
      {
            myCalculator.Change();      //算术表达式->逆波兰表达式

              m_sTest.Format("%lf",myCalculator.Calculate());   //计算逆波兰表达式得到结果
         UpdateData(false);  
       }
      else
        {
        MessageBox("Wrong Expresion! please input again!");
       }

        m_sTest="";
}


   封装计算功能的CMyCalculator类架构是

class CMyCalculator  
{
public:
      CMyCalculator(CString m_stest);    //
传入算术表达式

       ~CMyCalculator();   //析构释放vector占用的内存
        double Calculate();   //计算后缀表达式(逆波兰表达式)
        void Change();      //算数表达式转换为后缀表达式
      bool IsExpression();   //判断算术表达式是否正确
       bool IsOprand(char );  //判断运算符
        bool CheckBracket();  //判断括号的正确性
        int GetPriority(CString);   //获得运算符的优先级
        bool IsPriority(CString,CString);  //判断运算符之间的优先级大小  
private:
       CString m_string;   //算术表达式
        vector <CString> m_vOperator;  //运算符栈,压入运算符(包括括号)
        vector <CString> m_vOprand;   //操作数栈,压入操作数与运算符(不包括括号)
      vector <double> m_vResult;   //计算后缀表达式  
};

  

接下来是计算器的核心步骤,逆波兰算法的实现和计算

算术表达式->逆波兰表达式

//1.将运算符(含括号)压入m_vOperator,把操作数(含运算符)压入m_vOprand;

//2.遇'('直接压入运算符栈,遇')'把中间的运算符弹出到操作数栈;

//3.当前元素<栈顶元素时,栈顶元素的运算符压入运算符栈,当前元素再入操作数栈。
void CMyCalculator::Change()
{

        m_vOperator.push_back("#");
       for(int i=0;i<m_string.GetLength();i++)
       {
            //若为运算符或括号
          if(IsOprand(m_string.GetAt(i))||m_string.GetAt(i)=='('||m_string.GetAt(i)==')')
        {
                       // 若为 '('
                       if(m_string.GetAt(i)=='(')   
                        {

                               m_vOperator.push_back(m_string.GetAt(i));
          }
                   //若为')'
                   if(m_string.GetAt(i)==')')
          {
                              while(m_vOperator.back()!='(')
                           {
                                       m_vOprand.push_back(m_vOperator.back());
                        m_vOperator.pop_back();
             }
                m_vOperator.pop_back();
         }
          //若为运算符
                       if(m_string.GetAt(i)!='('&&m_string.GetAt(i)!=')')    
              {
             if(IsPriority(m_string.GetAt(i),m_vOperator.back()))
//若当前元素优先级>栈顶元素则入栈

                {
                               m_vOperator.push_back(m_string.GetAt(i));
                  }
                       else
 //若当前元素优先级<=栈顶元素
                          {
                                           m_vOprand.push_back(m_vOperator.back()); //栈顶元素压入操作数栈
                                    m_vOperator.pop_back();  //删除栈顶元素
                            m_vOperator.push_back(m_string.GetAt(i));  //当前元素压入运算符栈
                         }
                      }
           }
      //若为数字
          else
          {
          CString str;
        while((m_string.GetAt(i)>=48&&m_string.GetAt(i<=57||m_string.GetAt(i)=='.')

&&i<m_string.GetLength()-1)         //当为数字或小数点时累加成一个数字的字符串再压入操作数栈

                {
                          str+=m_string.GetAt(i);
                            i++;
          }
                if(i==m_string.GetLength()-1)    //  防止访问下表越界,而专为最后一位进行判断
                {
                    str+=m_string.GetAt(i);
                              i++;
          
                        m_vOprand.push_back(str);
           i--;      //for循环中会i++,所以这里得先减1
          }
       }
         //将运算符栈中剩余的运算符压入操作数栈
       while(m_vOperator.back()!="#")
       {
            m_vOprand.push_back(m_vOperator.back());
                m_vOperator.pop_back();
        }
}

计算逆波兰表达式(后缀表达式)

//将操作数栈中的后缀表达式依次取出,从左至右扫描

//若遇操作数压入m_vResult栈中

//若遇运算符(只含计算的运算符),则把栈顶俩元素弹出进行运算,把结果压入栈中

//最后在栈中保存的就只有一个元素的栈,把m_vResult[0]返回即可


   注意:我的计算器是用vector来操作,入栈,出栈只是形象化的比喻,还有vector的内存需要析构,不然会不断耗尽内存的,其次不能简单的析构,需要调用swap来把vector占用的内存给交换出来再析构,vector的clear只是清除数据,不能释放内存。








你可能感兴趣的:(vector,mfc,计算器,逆波兰表达式,CString)