第三次作业——计算器
预备知识
在自学的过程还未了解到关于queue
的知识,所以预先Google了一下相关知识。
queue
queue模板类的定义在
queue<int> q1;
queue<double> q2
queue基本操作
- push——入队:eg. que.push(x): 将x接到队列que的末端。
queue<string> que;
que.push("Hello World!");
que.push("OK");
- pop——出队:eg.que.pop():弹出队列的第一个元素即最先被插入的元素,注意,并不会返回被弹出元素的值。
que<string>que;
que.push("Hello World!");
que.push("OK");
que.pop(); //此时"Hello World"被删除
- size——访问队列中的元素个数。
queue<string> que;
cout<<que.size()<<endl;
que.push("Hello World!");
que.push("OK");
cout << que.size() << endl; //输出分别为“0”和“2”
- empty——判断队列是否空,如果队列为空,返回true。
queue<string> que;
cout << que.empty() << endl;
que.push("Hello World!");
que.push("OK");
cout << que.empty() << endl; //输出分别为“1”和“0”
- front——访问队首元素,即最早被压入队列的元素。
queue<string> que;
q.push("Hello World!");
q.push("OK");
cout << que.front() << endl;
que.pop();
cout << que.front() << endl; //输出为"Hello World"和"OK"
- back——访问队尾元素,即最后被压入队列的元素。
queue<string> que;
que.push("Hello World!");
que.push("OK");
cout << que.back() << endl; //输出为"OK"
思路
按照题目要求,将输入交给Scan类,输出交给Print类
- 头文件
#ifndef CALCULATOR_H_
#define CALCULATOR_H_
#include<string>
#include<queue>
using namespace std;
class Scan
{
friend istream &read(istream &, Scan &);
friend class Print;
private:
string ipt;
queue<string>que;
public:
void ToStringQueue(string input);
};
class Print
{
public:
void output(queue<string>q);
};
istream &read(istream &, Scan &);
#endif
- 实现文件
#include<iostream>
#include<string>
#include<queue>
#include "calculator.h"
using namespace std;
/***********************************
Description: 读取并扫描表达式,将数字与符号分开压入队列
Others: 当输入数学表达式中有数字(含小数位)超过10位即报错并终止程序
***********************************/
void Scan::ToStringQueue(string input)
{
int len = input.size();
for (int i;i<len;)
{
string tmp = "";
while (i < len && input[i] <= '9' && input[i] >= '0')
{
tmp += input[i++];
}
if(tmp.size() > 10)
{
cerr << "ERROR!";
while (!que.empty())
{
que.pop();
}
break;
}
if (!tmp.empty())
{
que.push(tmp);
}
while (i < len &&
(input[i] == '+' || input[i] == '-' || input[i] == '*' || input[i] == '/'
|| input[i] == '(' ||input[i] == ')'))
{
tmp = input[i++];
que.push(tmp);
}
}
}
/***********************************
Description : 输出队列中的数据
***********************************/
void Print::output(queue<string>q)
{
while (!q.empty())
{
cout << q.front() << endl;
q.pop();
}
}
/***********************************
Description:从给定流中 将数据读到给定的对象中
Others: 本意是将此函数包含至Scan类中,让读入的功能都让专门的类实现,
但是在实际操作过程中,确颇感困难,所以就独自定义了一个类相关的非成员函数
***********************************/
istream &read(istream &is, Scan &expr)
{
is >> expr.ipt;
return is;
}
- 客户文件
#include<iostream>
#include<string>
#include"calculator.h"
int main()
{
Scan in;
Print opt;
read(cin,in);
in.ToStringQueue(in.ipt);
opt.output(in.que);
return 0;
}
运行结果
程序分析
error中的信息提示我可能是通过非成员函数访问操作了私有部分的数据,但是回看代码的过程颇为迷茫,因为我将Print
类与read
函数都设为Scan
的友元,所以在试了多次改正的办法都没有解决,半天下来也有些心浮气躁,可能因为书此时看得还是颇少,掌握东西的量还不足,对于一些错误无法查找出来。在Google中也并未找到解决的方法。在自学有时还是颇感迷茫的,因为时常出了问题在搜索的基础上不足以解决,又无旁人的指点。这个问题也暂且放下,同时也在此作为一个保存,等再一步深入学习C++再回头来解决。
第二次尝试:
- 头文件:
#ifndef CALCULATOR_H_
#define CALCULATOR_H_
#include<string>
#include<queue>
using namespace std;
class Scan
{
private:
//string in;
friend istream &read(istream &, Scan &);
public:
string in;
queue<string>ToStringQueue(string input);
};
class Print
{
public:
void output(queue<string>q);
};
istream &read(istream &, Scan &);
#endif
- 实现文件:
#include<iostream>
#include<string>
#include<queue>
#include "calculator.h"
using namespace std;
/***********************************
Description: 读取并扫描表达式,将数字与符号分开压入队列
Others: 当输入数学表达式中有数字(含小数位)超过10位即报错并终止程序
***********************************/
queue<string>Scan::ToStringQueue(string input)
{
int len = input.size();
queue<string>que;
for (int i=0;i<len;)
{
string tmp = "";
while (i < len && input[i] <= '9' && input[i] >= '0')
{
tmp += input[i++];
}
if(tmp.size() > 10)
{
cerr << "ERROR!";
while (!que.empty())
{
que.pop();
}
break;
}
if (!tmp.empty())
{
que.push(tmp);
}
while (i < len &&
(input[i] == '+' || input[i] == '-' || input[i] == '*' || input[i] == '/'
|| input[i] == '(' ||input[i] == ')'))
{
tmp = input[i++];
que.push(tmp);
}
}
return que;
}
void Print::output(queue<string>q)
{
while (!q.empty())
{
cout << q.front() << endl;
q.pop();
}
}
/***********************************
Description:从给定流中 将数据读到给定的对象中
Others: 本意是将此函数包含至Scan类中,让读入的功能都让专门的类实现,
但是在实际操作过程中,确颇感困难,所以就独自定义了一个类相关的非成员函数
***********************************/
istream &read(istream &is, Scan &expr)
{
is >> expr.in;
return is;
}
- 客户文件
#include<iostream>
#include<string>
#include<queue>
#include"calculator.h"
int main()
{
Scan ipt;
Print opt;
queue<string>que;
read(cin,ipt);
que = ipt.ToStringQueue(ipt.in);
opt.output(que);
return 0;
}
运行结果
程序分析
经过第一次的失败(包含多次试验失败)又暂时没有有效的解决方法后,放弃了原有的思路。在ToStringQueue函数编写中不再使用void类型,因为没有返回值的同时一定要对private里面的队列数据进行访问,而此时编写的函数一直访问失败,所以直接让ToStringQueue函数返回一个队列,这样在使用Print类过程中就无需访问Scan类中的私有数据了。
反思
OPP编程思想的实现需要去努力理解并且融会贯通,虽然在之前学习有接触到这个思想,在之前博客关于链表实现的实例也有提到,但仍需加强理解。数据隐藏作为OPP编程思想的一大特点,虽然第一次尝试代码中有想着将数据隐藏起来,但是在实现中不尽人意,也没有解决的方案。
总结
这个作业前后花了不少时间,一开始的计划是学习关于类等语法知识,然后完成作业。但是在学习的过程中,一开始并没有动手自己实践一些代码,看书学习的这个过程总是萌生困意,对于类的知识也是迷迷糊糊,一知半解,浪费了不少时间。最后改变了计划,一边动手实践代码一边看书,但是这样也有着很大的弊端,因为在程序出问题的时候,没有对于类的总体把关,很难看出其中错误,搜索的过程也显得较为费力。总之,“试了很多错”,在这些错误中也了解到一些知识。希望在以后的学习过程中,在多多少少的“试错”过程中,能尽快找到合适自己的学习方法。现在回看这整个程序,好像都是简简单单的代码,实现也颇显简单,但是完成这份程序又确确实实用了不少时间。所以,这同时给了自己一个很大的警钟——路阻且长,仍需更加努力。
┆ 凉 ┆ 暖 ┆ 降 ┆ 等 ┆ 幸 ┆ 我 ┆ 我 ┆ 里 ┆ 将 ┆ ┆ 可 ┆ 有 ┆ 谦 ┆ 戮 ┆ 那 ┆ ┆ 大 ┆ ┆ 始 ┆ 然 ┆
┆ 薄 ┆ 一 ┆ 临 ┆ 你 ┆ 的 ┆ 还 ┆ 没 ┆ ┆ 来 ┆ ┆ 是 ┆ 来 ┆ 逊 ┆ 没 ┆ 些 ┆ ┆ 雁 ┆ ┆ 终 ┆ 而 ┆
┆ ┆ 暖 ┆ ┆ 如 ┆ 地 ┆ 站 ┆ 有 ┆ ┆ 也 ┆ ┆ 我 ┆ ┆ 的 ┆ 有 ┆ 精 ┆ ┆ 也 ┆ ┆ 没 ┆ 你 ┆
┆ ┆ 这 ┆ ┆ 试 ┆ 方 ┆ 在 ┆ 逃 ┆ ┆ 会 ┆ ┆ 在 ┆ ┆ 清 ┆ 来 ┆ 准 ┆ ┆ 没 ┆ ┆ 有 ┆ 没 ┆
┆ ┆ 生 ┆ ┆ 探 ┆ ┆ 最 ┆ 避 ┆ ┆ 在 ┆ ┆ 这 ┆ ┆ 晨 ┆ ┆ 的 ┆ ┆ 有 ┆ ┆ 来 ┆ 有 ┆
┆ ┆ 之 ┆ ┆ 般 ┆ ┆ 不 ┆ ┆ ┆ 这 ┆ ┆ 里 ┆ ┆ 没 ┆ ┆ 杀 ┆ ┆ 来 ┆ ┆ ┆ 来 ┆