第三次作业——计算器

作业链接
GitHub链接

预备知识

在自学的过程还未了解到关于queue的知识,所以预先Google了一下相关知识。

queue

queue模板类的定义在头文件中。queue模板类需要两个模板参数,一个是元素类型,一个容器类型,元素类型是必要的,容器类型是可选的,默认为deque类型。定义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编程思想的一大特点,虽然第一次尝试代码中有想着将数据隐藏起来,但是在实现中不尽人意,也没有解决的方案。

总结

这个作业前后花了不少时间,一开始的计划是学习关于类等语法知识,然后完成作业。但是在学习的过程中,一开始并没有动手自己实践一些代码,看书学习的这个过程总是萌生困意,对于类的知识也是迷迷糊糊,一知半解,浪费了不少时间。最后改变了计划,一边动手实践代码一边看书,但是这样也有着很大的弊端,因为在程序出问题的时候,没有对于类的总体把关,很难看出其中错误,搜索的过程也显得较为费力。总之,“试了很多错”,在这些错误中也了解到一些知识。希望在以后的学习过程中,在多多少少的“试错”过程中,能尽快找到合适自己的学习方法。现在回看这整个程序,好像都是简简单单的代码,实现也颇显简单,但是完成这份程序又确确实实用了不少时间。所以,这同时给了自己一个很大的警钟——路阻且长,仍需更加努力。

原文地址:https://www.cnblogs.com/ZhaoxiCheung/p/5206811.html