MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)

目录

一、创建VS文件

 二、对于计算器界面的创建

1.添加Edit Control

2.添加Static Text

3.添加Button

 三、对各按键的编写

1.事件处理函数

2.非特殊按键

3.特殊按键

(1)清零按键

(2)删除按键

(3)倒数运算

(4)等于号按键

四、运算函数的实现

1.Calculate.cpp文件

2.GetCalResult.cpp文件

3.transform.cpp文件

4.Calculator.h  文件(头文件)

五、计算示例

普通的复合运算

绝对值,倒数,次方的运算

小数的运算


需要工程文件,dy关注我私信免费获取(无任何条件),dy号:YyYxxxxxXxX___

一、创建VS文件

首先,打开vs,选择如图所示左边的MFC应用。更改项目名称并选择自己想存入的位置后,点击创建。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第1张图片

点击创建后,在应用程序类型中选择“基于对话框”,如下图。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第2张图片

其他名称无需更改。可以在生成的类中更改类名,如下图,我改为MFC_Calculator。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第3张图片

点击完成后来到这个界面。屏幕左侧有“工具箱”。工具箱里面有我们会用到的一些组件。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第4张图片

 二、对于计算器界面的创建

我的MFC计算器界面如下,这个也是这篇文章的最终目标。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第5张图片

可以看出,这个计算器具有整数和小数的加、减、乘、除等基本运算,也有倒数、次方、阶乘运算,以及有括号的复合运算,并具有清零(C),删除(Del)等功能。

接下来我们来一一创建。

1.添加Edit Control

在左侧的工具箱找到Edit Control组件,点击添加至我们的界面。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第6张图片

添加后,调整合适的大小。界面右下角的确定、取消按钮,可以单击右键,选择删除。调整完成后,如图

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第7张图片

2.添加Static Text

左侧工具箱中选择Static Text,添加至界面,并调整位置和大小。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第8张图片

此时,右键单击刚添加的Static Text,选择最下方的属性,将边框更改为True。如下图。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第9张图片

3.添加Button

将界面中间的“TODO:在此放置对话框控件。”右键单击删除。

左侧工具箱中选择Button,添加至界面。共添加24个Button。可以利用复制粘贴的方式快速添加。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第10张图片

添加后可以在VS的工具栏中找到对齐方式快速对齐

 然后一一更改每个Button的属性中的描述文字和ID

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第11张图片

数字1 位于界面左下方,所以左下方的Button的描述文字改为“1”,ID我改为“IDC_BUTTON_1”。

 更改完成后如下图。

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第12张图片

类似的,将其他Button的描述文字和ID进行更改。

以下为我对各按键的ID命名,Button的ID名称影响后续的编程。

2对应ID为IDC_BUTTON_2, 3对应ID为IDC_BUTTON_3,4对应ID为IDC_BUTTON_4                  5对应ID为IDC_BUTTON_5, 6对应ID为IDC_BUTTON_6,7对应ID为IDC_BUTTON_7                  8对应ID为IDC_BUTTON_8, 9对应ID为IDC_BUTTON_9,0对应ID为IDC_BUTTON_0            +对应ID为IDC_BUTTON_ADD, - 对应ID为IDC_BUTTON_SUB,*对应ID为IDC_BUTTON_MUL,    /对应ID为IDC_BUTTON_DIV,  |(绝对值符号)对应 ID为IDC_BUTTON_ABS,                              ^(次方)对应ID为IDC_BUTTON_POW,(对应ID为IDC_BUTTON_LBRACKET,                      )对应ID为IDC_BUTTON_RBRACKET,Del(删除按键)对应ID为IDC_BUTTON_DELETE,      C(清零按键)对应ID为IDC_BUTTON_CLEAR,1/x(倒数按键)对应ID为IDC_BUTTON_REM,!(阶乘按键)对应ID为IDC_BUTTON_FAC,.(小数点)对应ID为IDC_BUTTON_DOT,=(等于号)对应ID为IDC_BUTTON_EQUAL。

并将Edit Control的ID改为IDC_EDIT_EXP,Static Text的ID改为IDC_STATIC_RESULT

更改完成后,界面如下

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第13张图片

 三、对各按键的编写

双击界面中任意一个Button,来到了MFC_CalculatorDlg,cpp文件里面。

在该文件下,找出一个位置,写出按键处理函数

1.事件处理函数

void CMFCCalculatorDlg::AddToEditExp(UINT IDC_Button)
{
	CString strBtn;
	CString strExp;
	GetDlgItem(IDC_Button)->GetWindowText(strBtn);
	GetDlgItem(IDC_EDIT_EXP)->GetWindowText(strExp);
	SetDlgItemText(IDC_EDIT_EXP, strExp + strBtn);
}

2.非特殊按键

然后将各个按键的ID作为函数参数,实现各个Button,例如数字1,2

//‘1’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton1()
{
	AddToEditExp(IDC_BUTTON_1);
}

//‘2’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton2()
{
	AddToEditExp(IDC_BUTTON_2);
}

 以上方法用于左括号,有括号,加减乘除,0—9,绝对值,阶乘,次方,小数点的编写。

这些按键的完整代码如下

// 输入区显示按钮操作
void CMFCCalculatorDlg::AddToEditExp(UINT IDC_Button)
{
	CString strBtn;
	CString strExp;
	GetDlgItem(IDC_Button)->GetWindowText(strBtn);
	GetDlgItem(IDC_EDIT_EXP)->GetWindowText(strExp);
	SetDlgItemText(IDC_EDIT_EXP, strExp + strBtn);
}


// ‘(’左括号,按钮事件响应
void CMFCCalculatorDlg::OnBnClickedButtonLbracket()
{
	AddToEditExp(IDC_BUTTON_LBRACKET);
}


// ‘)’右括号,按钮事件响应
void CMFCCalculatorDlg::OnBnClickedButtonRbracket()
{
	AddToEditExp(IDC_BUTTON_RBRACKET);
}


//‘||’绝对值,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonAbs()
{
	AddToEditExp(IDC_BUTTON_ABS);
}


//‘!’阶乘,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonFac()
{
	AddToEditExp(IDC_BUTTON_FAC);
}


//‘^’幂次方,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonPow()
{
	AddToEditExp(IDC_BUTTON_POW);
}


//‘/’除法,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonDiv()
{
	AddToEditExp(IDC_BUTTON_DIV);
}


//‘*’乘法,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonMul()
{
	AddToEditExp(IDC_BUTTON_MUL);
}


//‘-’减法,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonSub()
{
	AddToEditExp(IDC_BUTTON_SUB);
}

//‘+’加法,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonAdd()
{
	AddToEditExp(IDC_BUTTON_ADD);
}
//‘.’小数点,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonDot()
{
	AddToEditExp(IDC_BUTTON_DOT);
}

//‘0’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton0()
{
	AddToEditExp(IDC_BUTTON_0);
}

//‘1’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton1()
{
	AddToEditExp(IDC_BUTTON_1);
}

//‘2’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton2()
{
	AddToEditExp(IDC_BUTTON_2);
}

//‘3’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton3()
{
	AddToEditExp(IDC_BUTTON_3);
}

//‘4’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton4()
{
	AddToEditExp(IDC_BUTTON_4);
}

//‘5’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton5()
{
	AddToEditExp(IDC_BUTTON_5);
}

//‘6’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton6()
{
	AddToEditExp(IDC_BUTTON_6);
}

//‘7’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton7()
{
	AddToEditExp(IDC_BUTTON_7);
}

//‘8’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton8()
{
	AddToEditExp(IDC_BUTTON_8);
}

//‘9’数字,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButton9()
{
	AddToEditExp(IDC_BUTTON_9);
}

3.特殊按键

(1)清零按键

按键实现代码

void CMFCCalculatorDlg::OnBnClickedButtonClear()
{
	SetDlgItemText(IDC_EDIT_EXP, NULL);
	CString cstr;
	cstr = "0";
	SetDlgItemText(IDC_STATIC_RESULT, cstr);
}

(2)删除按键

void CMFCCalculatorDlg::OnBnClickedButtonDelete()
{
	CString strExp;
	GetDlgItem(IDC_EDIT_EXP)->GetWindowText(strExp);
	strExp = strExp.Left(strExp.GetLength() - 1);
	SetDlgItemText(IDC_EDIT_EXP, strExp);
}

(3)倒数运算

由于倒数运算的特殊性,倒数不参与复合运算。所以可以在倒数按钮函数的上方定义字符串op、double类型数字num1和旗帜变量flag,然后进入函数,利用GetDlgItemText函数获取Eidt Control中的信息,并保存在字符串cs中,令num1等于_ttof(cs),op等于“1/x”,并将flag置为1。

具体代码如下:

double num1;
double result;
CString op;
int flag = 0;
//倒数,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonRem()
{
	CString cs;
	GetDlgItemText(IDC_EDIT_EXP, cs);

	num1 = _ttof(cs);

	op = "1/x";
	flag = 1;
}

(4)等于号按键

等于号按键含有计算结果,包含运算过程中各个函数,较为复杂。

首先在等于号对应函数上方定义一个double类型的结果变量result,进入函数后首先判断flag是否等于1,若等于,则进行倒数运算,否则进行其他运算。代码如下

double result;
//‘=’等于,按钮事件处理
void CMFCCalculatorDlg::OnBnClickedButtonEqual()
{
	if (flag == 1) {
		
		CString cs;
		GetDlgItemText(IDC_EDIT_EXP, cs);
		if (num1 != 0) {
			result = 1 / num1;
		}
		
		cs.Format(_T("%g"), result);
		SetDlgItemText(IDC_STATIC_RESULT, cs);
		flag = 0;
	}

	else {
		CString strExp;
		Calculator cal;		//计算类
		CString cstr_Result;
		CString cstr_ErrorInfo;

		GetDlgItem(IDC_EDIT_EXP)->GetWindowText(strExp);
		string infix(CW2A(strExp.GetString()));
		cal.calculate(infix);
		cstr_Result.Format(_T("%g"), cal.getResult());
		cstr_ErrorInfo + cal.getErrorImfo().c_str();
		if (!cstr_ErrorInfo.IsEmpty()) {
			SetDlgItemText(IDC_STATIC_RESULT, cstr_ErrorInfo);
		}
		SetDlgItemText(IDC_STATIC_RESULT, cstr_Result);
	}
}

可以看出,等于号中包含很多运算函数,接下来一一实现他们。

四、运算函数的实现

1.Calculate.cpp文件

Calculate.cpp文件主要对算术符号的优先权等级,初始化Calculator()函数,表达式自定义标准格式化,获取算术符号优先级,以及计算方法,获取结果等。

代码如下

#include "pch.h"
#include "Calculator.h"

#include 
#include 
#include 
#include 
#include 


using namespace std;


//算术符号优先权等级
enum PRIO_LV {
	PRIO_LV0 = 0,
	PRIO_LV1 = 1,
	PRIO_LV2 = 2,
	PRIO_LV3 = 3,
	PRIO_LV4 = 4,
};


Calculator::Calculator() {				//构造函数,初始化成员变量

	result = 0.0;
	//cal_ErrorImfo = "";
}


//表达式自定义标准格式化
void Calculator::getFormat(string infix) {

	stdInfix = infix;

	//实现正负数
	//for (int i = 0; i < stdInfix.length(); i++) {					//string下标调用运算符时可能会导致类型溢出
	for (size_t i = 0; i < stdInfix.size(); i++) {					//string.size()返回size_type类型,避免下标运算时的类型溢出
		if (stdInfix[i] == '-' || stdInfix[i] == '+') {				//-x转换为0-x,+x转化为0+x
			if (i == 0) {
				stdInfix.insert(0, 1, '0');
			}
			else if (stdInfix[i - 1] == '(') {
				stdInfix.insert(i, 1, '0');
			}
		}
	}
}

//获取算术符号优先级
int Calculator::getPrior(char c) {

	if (c == '+' || c == '-') {
		return PRIO_LV1;
	}
	else if (c == '*' || c == '/') {
		return PRIO_LV2;
	}
	else if (c == '%' || c == '^') {
		return PRIO_LV3;
	}
	else if (c == '!') {
		return PRIO_LV4;
	}
	else {
		return PRIO_LV0;
	}
    string str = "非法符号";
    cout << str << endl;

}

//计算方法
void Calculator::calculate(string infix) {
	getFormat(infix);			//表达式自定义标准格式化
	getPostfix();				//后缀表达式转换
	calResult();				//计算结果
}

//获取结果
double Calculator::getResult() {
	return result;
}

2.GetCalResult.cpp文件

GetCalResult.cpp文件主要是获取计算结果。

代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include "pch.h"
#include "Calculator.h"

#include 
#include 
#include 
#include 
#include 


void Calculator::calResult() {

	string tmp;
	double number = 0;
	double op1 = 0, op2 = 0;

	for (int i = 0; i < postfix.size(); i++) {
		tmp = postfix[i];
		if (tmp[0] >= '0' && tmp[0] <= '9') {
			number = atof(tmp.c_str());
			figStack.push(number);
		}
		else if (postfix[i] == "+") {
			if (!figStack.empty()) {
				op2 = figStack.top();
				figStack.pop();
			}
			if (!figStack.empty()) {
				op1 = figStack.top();
				figStack.pop();
			}
			figStack.push(op1 + op2);
		}
		else if (postfix[i] == "-") {
			if (!figStack.empty()) {
				op2 = figStack.top();
				figStack.pop();
			}
			if (!figStack.empty()) {
				op1 = figStack.top();
				figStack.pop();
			}
			figStack.push(op1 - op2);
		}
		else if (postfix[i] == "*") {
			if (!figStack.empty()) {
				op2 = figStack.top();
				figStack.pop();
			}
			if (!figStack.empty()) {
				op1 = figStack.top();
				figStack.pop();
			}
			figStack.push(op1 * op2);
		}
		else if (postfix[i] == "/") {
			if (!figStack.empty()) {
				op2 = figStack.top();
				figStack.pop();
			}
			if (!figStack.empty()) {
				op1 = figStack.top();
				figStack.pop();
			}
			if (op2 != 0) {
				///除数不为0,未做处理,默认
			}
			figStack.push(op1 / op2);
		}
		else if (postfix[i] == "^") {
			if (!figStack.empty()) {
				op2 = figStack.top();
				figStack.pop();
			}
			if (!figStack.empty()) {
				op1 = figStack.top();
				figStack.pop();
			}
			figStack.push(pow(op1, op2));
		}
		else if (postfix[i] == "|") {
			if (!figStack.empty()) {
				op1 = figStack.top();
				figStack.pop();
			}
			figStack.push(abs(op1));
		}
		else if (postfix[i] == "!") {
			if (!figStack.empty()) {
				op1 = figStack.top();
				figStack.pop();
			}
			if (op1 > 0) {
				//阶乘数应大于;为小数时(转化为整数求阶)
				double factorial = 1;
				for (int i = 1; i <= op1; ++i)
				{
					factorial *= i;
				}
				op1 = factorial;
			}
			figStack.push(op1);
		}
	}//end for
	if (!figStack.empty()) {
		result = figStack.top();
	}
}

3.transform.cpp文件

  transform.cpp文件主要是将中缀表达式转化为后缀表达式,该文件也是整个工程最为重要最为复杂的一部分,这里我们着重介绍。

  对于一般的,在数学上的,所有的计算表达式均是中缀表达式(这体现在运算符号一般在数字的中间)如对于表达式2+3,其中的加号在数字2,3的中间。将其转化为后缀表达式就是2 3 + ,体现在加号在两个运算数的后面。

  计算机是执行命令的机器,对于中缀表达式,当表达式中有括号,乘法,加法等的复合运算,计算机就需要多次遍历表达式,从而先计算括号等优先级较高的运算符,因此将会大大降低运算效率,所以人们发明出后缀表达式,只需从左向右遍历一遍即可,契合计算机,提高了运算效率。

  计算机从左向右遍历表达式,遇到数字进栈,遇到运算符就退出栈中两个数字,先退出的在运算符前面,后退出在运算符后面,进行计算,然后将运算结果压入栈中,继续遍历表达式,最终栈中就有一个数字,就为运算结果。

代码如下:

#include "pch.h"
#include "Calculator.h"

#include 
#include 
#include 
#include 
#include 


//绝对值符号个数的奇偶性
enum ABS_ODEVITY {
	ABS_ODD = 1,
	ABS_EVEN = 2,
};
void Calculator::getPostfix() {

	int absNumeber = ABS_ODD;				//绝对值符号个数的奇偶性
	string tmp;

	//for (int i = 0; i < stdInfix.length(); i++) {
	for (size_t i = 0; i < stdInfix.size(); i++) {					//string.size()返回size_type类型,避免下标运算时的类型溢出
		tmp = "";
		switch (stdInfix[i]) {
		case '+':
		case '-':
		case '*':
		case '/':
		case '^':
		case '!':
			if (symStack.empty() || symStack.top() == '(' || symStack.top() == '[' || symStack.top() == '{' || (symStack.top() == '|' && absNumeber == ABS_ODD)) {
				symStack.push(stdInfix[i]);
			}
			else {
				while (!symStack.empty() && (getPrior(symStack.top()) >= getPrior(stdInfix[i]))) {
					tmp += symStack.top();
					postfix.push_back(tmp);
					symStack.pop();
					tmp = "";
				}
				symStack.push(stdInfix[i]);
			}
			break;
		case '|':
			if (absNumeber == ABS_ODD) {
				symStack.push(stdInfix[i]);
				absNumeber = ABS_EVEN;
			}
			else {
				while (!symStack.empty() && symStack.top() != '|') {
					tmp += symStack.top();
					postfix.push_back(tmp);
					symStack.pop();
					tmp = "";
				}
				if (!symStack.empty() && symStack.top() == '|') {
					tmp += symStack.top();
					postfix.push_back(tmp);						//左绝对值符号'|'加入后缀表达式,用于绝对值的检测计算
					symStack.pop();
					absNumeber = ABS_ODD;
				}
			}
			break;
		case '(':
		case '[':
		case '{':
			symStack.push(stdInfix[i]);
			break;
		case ')':
			while (!symStack.empty() && symStack.top() != '(') {
				tmp += symStack.top();
				postfix.push_back(tmp);
				symStack.pop();
				tmp = "";
			}
			if (!symStack.empty() && symStack.top() == '(') {
				symStack.pop();							//将左括号出栈丢弃
			}
			break;
		case ']':
			while (!symStack.empty() && symStack.top() != '[') {
				tmp += symStack.top();
				postfix.push_back(tmp);
				symStack.pop();
				tmp = "";
			}
			if (!symStack.empty() && symStack.top() == '[') {
				symStack.pop();							//将左括号出栈丢弃
			}
			break;
		case '}':
			while (!symStack.empty() && symStack.top() != '{') {
				tmp += symStack.top();
				postfix.push_back(tmp);
				symStack.pop();
				tmp = "";
			}
			if (!symStack.empty() && symStack.top() == '{') {
				symStack.pop();							//将左括号出栈丢弃
			}
			break;
		default:
			if ((stdInfix[i] >= '0' && stdInfix[i] <= '9')) {
				tmp += stdInfix[i];
				while (i + 1 < stdInfix.length() && (stdInfix[i + 1] >= '0' && stdInfix[i + 1] <= '9' || stdInfix[i + 1] == '.')) {		//小数处理

					tmp += stdInfix[i + 1];			//是连续的数字,则追加
					i++;
				}
				if (tmp[tmp.length() - 1] == '.') {
					tmp += '0';						//将x.做x.0处理
				}
				postfix.push_back(tmp);
			}
			break;
		}
	}

	//if(!symStack.empty()) {
	while (!symStack.empty()) {						//将栈中剩余符号加入后缀表达式
		tmp = "";
		tmp += symStack.top();
		postfix.push_back(tmp);
		symStack.pop();
	}
}

4.Calculator.h  文件(头文件)

代码如下:

#pragma once

#include 
#include 
#include 
using namespace std;

//计算器类
class Calculator
{
public:
	Calculator();
	void calculate(string infix);		//计算方法
	void getFormat(string infix);		//表达式自定义标准格式化
	int getPrior(char c);				//获取算术符号优先级
	void getPostfix();					//后缀表达式转换
	void calResult();					//计算结果
	double getResult();					//获取结果
	string operatorSym;					//运算符号

private:
	vector postfix;				//后缀表达式向量
	stack symStack;				//符号栈
	stack figStack;				//数字栈
	string stdInfix;					//自定义标准格式化表达式
	double result;						//最终计算结果
};

五、计算示例

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第14张图片

普通的复合运算

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第15张图片

绝对值,倒数,次方的运算

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第16张图片MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第17张图片

 MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第18张图片

 小数的运算

MFC计算器——万字长文/一看就懂(需要的建议点赞收藏)_第19张图片

你可能感兴趣的:(mfc,c++,小程序,交互)