当前课程知识点:C++语言程序设计进阶 >  第九章 模板与群体数据 >  栈 >  例9-9 栈的应用

返回《C++语言程序设计进阶》慕课在线视频课程列表

例9-9 栈的应用在线视频

例9-9 栈的应用

例9-9 栈的应用

例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++语言程序设计进阶》慕课在线视频列表

例9-9 栈的应用课程教案、知识点、字幕

大家好

欢迎回来继续学习

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函数就可以了

所以主函数的逻辑都是非常简单

C++语言程序设计进阶课程列表:

第七章 继承与派生

-导学

--导学

-继承的基本概念和语法

--继承的基本概念和语法

-第七章 继承与派生--继承的基本概念和语法习题

-继承方式

--继承方式简介及公有继承

--私有继承和保护继承

-第七章 继承与派生--继承方式

-基类与派生类类型转换

--基类与派生类类型转换

-第七章 继承与派生--基类与派生类类型转换

-派生类的构造和析构

--派生类的构造函数

--派生类的构造函数举例

--派生类的复制构造函数

--派生类的析构函数

--第七章 继承与派生--派生类的构造和析构

-派生类成员的标识与访问

--访问从基类继承的成员

--虚基类

-第七章 继承与派生--派生类成员的标识与访问

-小结

--小结

-综合实例

--第七章综合实例

-实验七

--实验七

-第七章讲义

第八章 多态性

-导学

--导学

-第八章 多态性--导学

-运算符重载

--运算符重载的规则

--双目运算符重载为成员函数

--单目运算符重载为成员函数

--运算符重载为非成员函数

-第八章 多态性--运算符重载

-虚函数

--虚函数

--虚析构函数

--虚表与动态绑定

-第八章 多态性--虚函数

-抽象类

--抽象类

--第八章 多态性--抽象类

-override与final

--override与final

-第八章 多态性--override与final

-小结

--第八章小结

-综合实例

--第八章综合实例

-实验八

--实验八

- 第八章讲义

第九章 模板与群体数据

-导学

--导学

-模板

--函数模板

--类模板

-第九章 模板与群体数据--模板

-线性群体

--线性群体的概念

-第九章 模板与群体数据--线性群体

-数组

--数组类模板

--例9-4数组类应用举例

-链表

--链表的概念与结点类模板

--链表类模板

-第九章 模板与群体数据--链表

-栈

--栈类模板

--栈类模板课后习题

--例9-9 栈的应用

--例9-9 栈的应用课后习题

-队列

--队列类模板

-第九章 模板与群体数据--队列

-排序

--排序概述

--插入排序

--选择排序

--交换排序

-第九章 模板与群体数据--排序

-查找

--查找

--查找课后习题

-小结

--小结

-综合实例

--综合实例

-实验九

--实验九

- 第九章讲义

第十章 泛型程序设计与C++标准模板库

-导学

--导学

-泛型程序设计及STL的结构

--泛型程序设计的基本概念

--STL简介

-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构

-迭代器

--迭代器

-第十章 泛型程序设计与C++标准模板库--迭代器

-容器的基本功能与分类

--容器的基本功能与分类

-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类

-顺序容器

--顺序容器的基本功能

--顺序容器的特征

--顺序容器的插入迭代器与适配器

--第十章 泛型程序设计与C++标准模板库--顺序容器

-关联容器

--关联容器分类和基本功能

--集合

--映射

--多重集合和多重映射

-第十章 泛型程序设计与C++标准模板库--关联容器

-函数对象

--函数对象

--函数适配器

-算法

--算法

-小结

--第十章小结

-综合实例

--综合实例

-实验十

--实验十

- 第十章讲义

第十一章 流类库与输入/输出

-导学

--导学

-I/O流的概念及流类库结构

--I/O流的概念及流类库结构

-第十一章 流类库与输入/输出--I/O流的概念及流类库结构

-输出流

--输出流概述

--向文本文件输出

--向二进制文件输出

--向字符串输出

-第十一章 流类库与输入/输出--输出流

-输入流

--输入流概述

--输入流应用举例

--从字符串输入

-第十一章 流类库与输入/输出--输入流

-输入/输出流

--输入/输出流

-第十一章 流类库与输入/输出--输入/输出流

-小结

--小结

-综合实例

--综合实例

-实验十一

--实验十一

- 第十一章讲义

第十二章 异常处理

-导学

--第12章导学

-异常处理的思想与程序实现

--异常处理的思想与程序实现

-第十二章 异常处理--异常处理的思想与程序实现

-异常处理中的构造与析构

--异常处理中的构造与析构

-第十二章 异常处理--异常处理中的构造与析构

-标准程序库异常处理

--标准程序库异常处理

-第十二章 异常处理--标准程序库异常处理

-小结

--第12章小结

-综合实例

--综合实例

-实验十二

--实验十二

- 第十二章讲义

例9-9 栈的应用笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。