2017《面向对象程序设计》课程作业六

题目描述

  • 本次作业要求将四则运算的核心部分采取栈的知识进行解决。即表达式生成的合法性检验、表达式结果计算。
  • 学习C++界面编程,可以学QT、MFC或者VS,选择其一即可,用博客记录学习到的知识以及心得体会。

作业要求

  • 本次作业要求实现核心算法,请将表达式生成的代码及相关的检验、计算表达式结果的代码贴在博客中,并对代码进行必要的解释。
  • 发表一篇博客,博客内容为:提供本次作业的github链接,本次程序运行的截图,对界面编程的探索。
  • 作业链接

完整代码链接

代码节选

表达式部分

思路

  • 这次表达式的存储、检验和输出都是使用栈来完成的。
  • 为了后面表达式格式的检验,即确保表达式是如(a+b)/(c-d)的形式,不会出现“)a+b)”或是其他错乱的情况,所有的数字和运算符都以字符的形式存储,这样便于每个部分的检验。
  • 参考16进制,把占用两个字符的10处理成只占用一个字符的'A'。这样表达式所有的元素都是以字符的形式存储,而且表达式长度为11个字符。
  • 过程:将随机生成的运算符、数字进行检验后(包括除数不为0,不出现小数结果),都push入栈,复制一份进行格式检验,再将表达式pop出来。
if (j == 0)
	{
		sta.push("=");
		sta.push(")");
		if (d == 10)sta.push("A");//对10的处理,push数字进栈
		else
		{
			sprintf(temp, "%d", d);
			sta.push(temp);
		}
		sprintf(temp, "%c", sign2);//push运算符进栈
		sta.push(temp);
		}



	stack tempsta;//复制一份表达式
	tempsta.initstack();
	tempsta = sta;
	


	while (tempsta.isempty())
	{
		char t,L=0;
		
		if (tempsta.isempty()) { j = true; break; }//检验括号
		else
		{
			t = tempsta.pop();
			if (t == '(') L++;
			else { j = true; break; }
			}
		
		if (tempsta.isempty()) { j = true; break; }//检验数字
		else 
		{ 
			t = tempsta.pop();
		    if ((t >= '0'&&t <= '9') || t == 'A')L++; 
		    else { j = true; break; }
		}
		
		if (tempsta.isempty()) { j = true; break; }//检验运算符
		else
		{	t = tempsta.pop();
			if (t=='+'||t=='-'||t=='*'||t=='/')L++;
			else { j = true; break; }
		}
		
		

		if (L != 11 )j = true;//检验表达式长度

//主函数中输出表达式
while (ex.sta.isempty() == false)
		{
			t = ex.sta.pop();
			if (t == 'A')
				cout << "10";
			else
			cout << t;
		}

计算部分

  • 这次修改,加强了类的封装。在主函数中接受一个表达式对象,输出一个表达式的值,简化了使用。
  • 接受栈以后,把栈的运算符和数字传提取出来,计算后return 最后的结果。
int Calculator::total_result(stack & ex)
{
	int x1, x2, x3, x4, count_ab = 0, count_cd = 0;//以下是提取表达式的数字和运算符。
	char temp,sign1,sign2,sign3;
	temp = ex.pop();

	temp = ex.pop();
	if (temp == 'A')x1 = 10;
	else x1 = temp - '0';
	
	sign1 = ex.pop();
	
	temp = ex.pop();
	if (temp == 'A')x2 = 10;
	else x2 = temp - '0';
	
	temp = ex.pop();

	sign2 = ex.pop();
	
	temp = ex.pop();

	temp = ex.pop();
	if (temp == 'A')x3 = 10;
	else x3 = temp - '0';

	sign3 = ex.pop();
	
	temp = ex.pop();
	if (temp == 'A')x4 = 10;
	else x4 = temp - '0';
//以下是答案的计算
	this->getnum(x1, x2);
	count_ab = this->calculateResult(sign1);
	this->getnum(x3, x4);
	count_cd = this->calculateResult(sign3);
	this->getnum(count_ab, count_cd);
	return this->calculateResult(sign2);
}


//计算部分
void Calculator::getnum(int a1, int a2)
{
	a = a1;
	b = a2;
}
int Calculator::calculateResult(char sign)
{
	switch (sign)//注意除数不能为0,整数输出 
	{
	case '+':result = a + b; break;
	case '-':result = a - b; break;
	case '*':result = a*b; break;
	case '/':result = a / b; break;
	}
	return result;
}

运行示例

对界面编程的探索

void CMFCcountDlg::OnBnClickedButton1()
{
	UpdateData();
	Expression Ex;
	mex = " ";
	while (1)
	{
		Ex.getsign();
		Ex.randomNumber();
		if (Ex.generateExpression())continue;
		for (int i = 0; i < 11; i++)
		{
			if (Ex.ch[i] == 'A')
			{
				mex += "10";
			}
			else
			{
				mex += Ex.ch[i];
			}
		}
		for (int i = 0; i < 10; i++)
		{
			EX[i] = Ex.ch[i];
		}
		break;
	}
	UpdateData(FALSE);
	
}


void CMFCcountDlg::OnBnClickedButton2()
{
	UpdateData();
	Calculator cal;
	Counter cou;
	int rightanswer;
	mrightanswer=rightanswer = cal.total_result(EX);
	cou.getanswer(manswer, rightanswer);
	mresult = " ";
	if (cou.judge())
	{
		mresult += "You're right!\n";
		cou.count_right_answer();
		mnumber++;
	}
	else {
		mresult += "It's wrong.";
	}
	UpdateData(FALSE);
}

遇到问题

  • 创建mfc应用程序的时候,出现 “无法找到资源编译器dll,请确保路径正确”的提示框。
  • 解决办法:在所有文件中搜索出rcdll.dll,然后将这个文件放入c:\program.files.(x86)\Microsoft.Visual.Studio.10.0\vc\bin\中(或者是提示框中的路径)。
  • 在.cpp文件中各个头文件的最后添加了#include "stdafx.h"后,会出现已经声明并且定义了类、变量、函数,但是在编译的时候显示无法找到。
  • 解决办法:将#include "stdafx.h"作为第一个头文件。
  • 上网查了以后才知道:编译器通过一个头文件stdafx.h来使用预编译头文件。stdafx.h这个头文件名是可以在project的编译设置里指定的。编译器认为,所有在指令#include "stdafx.h"前的代码都是预编译的,它跳过#include "stdafx.h"指令,使用projectname.pch编译这条指令之后的所有代码。因此,所有的MFC实现文件第一条语句都是:#include "stdafx.h"。在它前面的所有代码将被忽略,所以其他的头文件应该在这一行后面被包含。否则,你将会得到“No such file or directory”这样让你百思不得其解的错误提示。

体会

  • 这次作业的完成是一个不断遇到问题、解决问题的过程,遇到的问题远比上述的多,但是最后做出来这个四则运算器,还是很开心的。
  • 将DOS窗口改成MFC,代码的改动不亚于重写一份代码,需要考虑变量新的特性,比如从窗口接受的变量各个函数都可以使用,使用UpdateData()时所有窗口的变量都会更新。我删除了两个大的部分,一个是交互窗口,一个是主函数,而且计数的过程也更加简洁。但是设置了静态变量略微破坏了类的封装性,是一个让我纠结的问题。
  • 由于没有系统地学过MFC,所以完成的过程类似于半学习半尝试,一步一步地将这个四则运算器做出来。虽然界面和功能都很简单,但是将原来DOS窗口的代码移植到MFC中并不是一件简单的事。由于没有主函数,每一个button相当于一个自定义函数,点击后就运行这个自定义函数。这样做,面向对象的感觉就更明显,每个button都是独立的,button与button之间需要消息来联系。
  • 就比如这个四则运算软件,点击“出题”,就显示出一个算式,点击“运算”就要将这个算式算出来。“出题”和“运算”这两个button就需要联系。因为对MFC中的数据类型不是特别的了解,所以我建立了一个静态变量作为“消息”用于传递生成的表达式。而这次作业要求修改的代码也正好方便了我在MFC中的计算功能,我只需要传递那个表达式,就可以输出一个正确答案。
posted @ 2017-05-30 18:28  范加索尔拉  阅读(424)  评论(2编辑  收藏  举报