当前课程知识点:C++语言程序设计进阶 > 第九章 模板与群体数据 > 栈 > 例9-9 栈的应用
实现一个简单的整数计算器,能够进行加、减、乘、除和乘方运算。使用时算式采用后缀输入法,每个操作数、操作符之间都以空白符分隔。例如,若要计算"3+5"则输入"3 5 +"。乘方运算符用"^"表示。每次运算在前次结果基础上进行,若要将前次运算结果清除,可键入"c"。当键入"q"时程序结束。
//Calculator.h #ifndef CALCULATOR_H #define CALCULATOR_H #include "Stack.h" // 包含栈类模板定义文件 class Calculator { //计算器类 private: Stack<double> s; // 操作数栈 void enter(double num); //将操作数num压入栈 //连续将两个操作数弹出栈,放在opnd1和opnd2中 bool getTwoOperands(double &opnd1, double &opnd2); void compute(char op); //执行由操作符op指定的运算 public: void run(); //运行计算器程序 void clear(); //清空操作数栈 }; #endif //CALCULATOR_H //Calculator.cpp #include "Calculator.h" #include <iostream> #include <sstream> #include <cmath> using namespace std; //工具函数,用于将字符串转换为实数 inline double stringToDouble(const string &str) { istringstream stream(str); //字符串输入流 double result; stream >> result; return result; } void Calculator::enter(double num) { //将操作数num压入栈 s.push(num); } bool Calculator::getTwoOperands(double &opnd1, double &opnd2) { if (s.isEmpty()) { //检查栈是否空 cerr << "Missing operand!" << endl; return false; } opnd1 = s.pop(); //将右操作数弹出栈 if (s.isEmpty()) { //检查栈是否空 cerr << "Missing operand!" << endl; return false; } opnd2 = s.pop(); //将左操作数弹出栈 return true; } void Calculator::compute(char op) { //执行运算 double operand1, operand2; bool result = getTwoOperands(operand1, operand2); if (result) { //如果成功,执行运算并将运算结果压入栈 switch(op) { case '+': s.push(operand2 + operand1); break; case '-': s.push(operand2 - operand1); break; case '*': s.push(operand2 * operand1); break; case '/': if (operand1 == 0) { //检查除数是否为0 cerr << "Divided by 0!" << endl; s.clear(); //除数为0时清空栈 } else s.push(operand2 / operand1); break; case '^': s.push(pow(operand2, operand1)); break; default: cerr << "Unrecognized operator!" << endl; break; } cout << "= " << s.peek() << " "; //输出本次运算结果 } else s.clear(); //操作数不够,清空栈 } void Calculator::run() { //读入并处理后缀表达式 string str; while (cin >> str, str != "q") { switch(str[0]) { case 'c': s.clear(); break; case '-': //遇'-'需判断是减号还是负号 if (str.size() > 1) enter(stringToDouble(str)); else compute(str[0]); break; case '+': //遇到其它操作符时 case '*': case '/': case '^': compute(str[0]); break; default: //若读入的是操作数,转换为整型后压入栈 enter(stringToDouble(str)); break; } } } void Calculator::clear() { //清空操作数栈 s.clear(); } //9_9.cpp #include "Calculator.h" int main() { Calculator c; c.run(); return 0; }
大家好
欢迎回来继续学习
C++语言程序设计
接下来呢
我们再通过一个简单的例子
来体验一下
我们怎么样使用
刚刚自己定义的那个栈类的模板
我们来写一个简单的
计算器的程序
在这里呢
我们就要用到栈
那么这个简单的整数计算器呢
能够进行加减乘除和乘方运算
使用的时候
这个算式要采用后缀输入法
这个看起来好象不太舒服
但是这样写
我们的程序实现比较简单一些
每个操作数
操作符之间还要用空白符来分割
比如说
我们要计算3+5的
那我们要输入3空格5
空格+号
乘方运算符呢
就用这个尖尖符号来表示
每一次运算
都前一次运算的结果的基础上
进行
如果我们要将
前次运算的结果清除的话呢
就输入一个c
那么就以前的结果就清除了
什么时候结束呢
当我们输入一个q的时候
这个程序就结束了
接下来呢
我们来看这个源程序代码
好 我们来看这个计算器类
先看它的公有接口
只有两个函数
一个是run
运行计算器函数
一个是clear
那么实际上呢
只要调用这个run
你就可以往里面输入数据
和运算符得到运算结果
那么这个run的函数呢
自己完成那么多复杂功能
这个函数肯定太庞大了
所以它需要一些其他的
辅助函数来协助它完成
这个计算工作
这些辅助函数呢
只为run服务
所以它不必写成公有的
写成私有的就可以了
在外部并不调用它们
那我们看这有哪些辅助函数
一个是enter
将操作数压入栈的函数
getTwoOperands
这是从栈里面弹出两个操作数
这个函数
还有呢compute
是执行由op指定的运算
那我们来看看
它的数据成员有什么呢
就是一个操作数栈
这是一个计算器
每次就计算一个
就执行一个操作
所以它的操作符就不用入栈了
但是呢
你执行一个操作的时候
有的时候它会有两个操作数
算术运算都是二元的了
还有呢
我们的运算
可以在前一次的运算结果之上
继续执行
所以操作数还是需要一个栈的
这就设计了一个操作数栈
接下来
我们来看这个函数的实现
这里我们需要一个工具函数
将字符串转换为实数
因为我们要从键盘输入
在一行上又输入运算符
又输入操作数
那么有字符 有数字
怎么办呢
那么我们统一都用字符串的方式
来读入
如果它是数字的话呢
我们就可以给它转成实数
所以在这儿
我们用到了
一个字符串输入流来做转换
这个为什么字符串输入流
可以进行转换呢
我简单说一下这个道理
就是将这个字符串
作为你读取数据的这个来源
而不是将键盘
那这个跟我们从键盘里面
读取数据是一样的
这个在第十一章会详细介绍
现在我们可以不管
这个函数体内部实现
是怎么实现的
就只知道有这么一个
stringToDouble函数
我们可以拿来用就可以了
好 现在看这个enter
这个函数将参数的这个num呢
压到栈里面
直接就调用push就可以了
调用push
把它压到s这个栈里面就可以了
然后这个是取两个操作数出来
那取两个操作数出来
也就是它的结果应该是两个数
那就不能够用return
给出这两个数了
所以呢
用的是两个引用作参数
引用作参数
它既可以带入需要计算的数据
又可以带出 传出结果
那么这一次
我们需要用这两个参数传出结果
传出两个操作数来
好
那首先看栈不空的情况下
调用一次pop
从s栈里面弹出一个操作数
看看这儿还空不空了
如果说这中间栈空了
那就要返回false
没有成功地取出两个操作数
那这个时候呢
后续的操作就不会继续了
好
不空的时候
再弹出第二个操作数
两个操作数
如果都成功弹出了
就是retunr true
表示弹出操作数这个操作
正确了 完成了
再看最主要的部分
是执行计算这个部分
执行计算呢
我们首先
你既然要计算
就要取两个操作数出来 对吧
这个操作符呢
在参数里面给它了
取两个操作数出来
取出的两个操作数
然后看到底要进行什么计算
如果是加减乘计算
那就直接运算就可以了
直接运算就可以了
如果是除法运算
那就要检查除数是否为0
除数为0呢
那就清空栈
这个运算不能继续下去了
除数不为0的情况下
执行这个除法
如果是这样一个乘方的运算
那么就调用这个pow函数
去做乘方
如果是其他的符号
取出来的
给的操作符如果是其他符号
那就是出错了 break出来
计算完了以后
输出一下本次的运算结果
这个运算结果呢
我们要输出就调用这peak
读取一下这个栈里的元素就行了
因为运算结果push到栈里面以后
我们输出这个运算结果
并不要把它删除
你完成了一次运算以后
下次运算也许还要用呢
对吧
如果说取出这个操作数的结果
为假
就说明没有成功地
取出两个操作数
那这个时候操作数不够
清楚栈进行运算
好 现在我们来看这个run函数
run函数它完全是读入
这个后缀表达式并且处理它
好 那现在呢
从键盘输入一个字符串
输入一个字符串
然后比较一下
这个字符串不是q
q就退出了
大家看这是一个逗号运算
逗号运算
先执行第一个表达式
再执行第二个表达式
以第二个表达式的结果
作为整个结果
所以先输入一个字符串
然后判断它不等于q
这种情况下
就执行这个循环体内容
执行循环体内容呢
然后要看一下
读入的这个字符串
它的首字符是什么
如果首字符是c
那调用clear去清楚栈
如果首字符是减号
那到底是减号还是负号呢
这个时候
我们判断一下这个字符串长度
如果它是大于1
就说明它是负号
它是负号呢
就是说现在读入的是一个负数
是一个数据
那就将这个数调用stringToDouble
转换成一个double数据
然后把它压到栈里面去
如果读入的这个字符串
长度是1
说明读入的是个减号
读入减号了
我们就该进行计算了
所以这个时候调用compute
调用compute
把读入的这个减号
作为参数传进去
它开始计算
那么如果遇到的是其他的操作符
加乘除 乘方
那就直接调用compute
执行计算就可以了
将这个操作符作为参数传过去
其他情况呢我们认为
读入的都是数字
那我们把它转换成double
压入栈里面去
这个时候呢
实际上在这个程序里面
我们考虑的比较简单
在这个输入的时候
假设用户都给了
正确的数据
我们才计算下去
如果用户给的数据不正确呢
在后面计算过程中就会停止
那再看最后一个函数呢
就是清空栈
接下来主函数中呢
我们看
构造一个计算器类的对象
然后调用它的run函数就可以了
所以主函数的逻辑都是非常简单
-导学
--导学
-继承的基本概念和语法
-第七章 继承与派生--继承的基本概念和语法习题
-继承方式
-第七章 继承与派生--继承方式
-基类与派生类类型转换
-第七章 继承与派生--基类与派生类类型转换
-派生类的构造和析构
--派生类的构造函数
--派生类的析构函数
--第七章 继承与派生--派生类的构造和析构
-派生类成员的标识与访问
--虚基类
-第七章 继承与派生--派生类成员的标识与访问
-小结
--小结
-综合实例
--第七章综合实例
-实验七
--实验七
-导学
--导学
-第八章 多态性--导学
-运算符重载
--运算符重载的规则
-第八章 多态性--运算符重载
-虚函数
--虚函数
--虚析构函数
--虚表与动态绑定
-第八章 多态性--虚函数
-抽象类
--抽象类
--第八章 多态性--抽象类
-override与final
-第八章 多态性--override与final
-小结
--第八章小结
-综合实例
--第八章综合实例
-实验八
--实验八
- 第八章讲义
-导学
--导学
-模板
--函数模板
--类模板
-第九章 模板与群体数据--模板
-线性群体
--线性群体的概念
-第九章 模板与群体数据--线性群体
-数组
--数组类模板
-链表
--链表类模板
-第九章 模板与群体数据--链表
-栈
--栈类模板
--栈类模板课后习题
--例9-9 栈的应用课后习题
-队列
--队列类模板
-第九章 模板与群体数据--队列
-排序
--排序概述
--插入排序
--选择排序
--交换排序
-第九章 模板与群体数据--排序
-查找
--查找
--查找课后习题
-小结
--小结
-综合实例
--综合实例
-实验九
--实验九
- 第九章讲义
-导学
--导学
-泛型程序设计及STL的结构
--STL简介
-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构
-迭代器
--迭代器
-第十章 泛型程序设计与C++标准模板库--迭代器
-容器的基本功能与分类
-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类
-顺序容器
--顺序容器的特征
--第十章 泛型程序设计与C++标准模板库--顺序容器
-关联容器
--集合
--映射
-第十章 泛型程序设计与C++标准模板库--关联容器
-函数对象
--函数对象
--函数适配器
-算法
--算法
-小结
--第十章小结
-综合实例
--综合实例
-实验十
--实验十
- 第十章讲义
-导学
--导学
-I/O流的概念及流类库结构
-第十一章 流类库与输入/输出--I/O流的概念及流类库结构
-输出流
--输出流概述
--向文本文件输出
--向二进制文件输出
--向字符串输出
-第十一章 流类库与输入/输出--输出流
-输入流
--输入流概述
--输入流应用举例
--从字符串输入
-第十一章 流类库与输入/输出--输入流
-输入/输出流
--输入/输出流
-第十一章 流类库与输入/输出--输入/输出流
-小结
--小结
-综合实例
--综合实例
-实验十一
--实验十一
- 第十一章讲义
-导学
--第12章导学
-异常处理的思想与程序实现
-第十二章 异常处理--异常处理的思想与程序实现
-异常处理中的构造与析构
-第十二章 异常处理--异常处理中的构造与析构
-标准程序库异常处理
-第十二章 异常处理--标准程序库异常处理
-小结
--第12章小结
-综合实例
--综合实例
-实验十二
--实验十二
- 第十二章讲义