前言
算是纪念自己头一次开始结合自己所学写简单的“小项目”,实际上还是很“菜”的。
开始时无论是对于编程语言本身的掌握(目前学习到的C++和Java),还是有一个大概的框架规划,我都感觉自己无从下手,因此也去“临时抱佛脚”再学了一些,CSDN 也好,B站也罢,总之确实学到的不少,因此记录下来,算是见证自己的进步吧!
前期准备
一、MFC的基本介绍
百度百科的解释:MFC全称为Microsoft Foundation Classes ,是微软公司提供的一个类库,以C++的形式封装了Windows API,并且包含一个应用程序框架,便于应用程序开发人员使用!
为了实现用C++编写实现可视化的简单计算器,我选择Visual Studio 2019版本中的MFC来开发这个小项目,此外还参考了B站的MFC基本操作的视频,用以更好的帮助自己完成项目!
二、算术表达式
1、算术表达式的特点:
(1)操作数之间的相对次序不变;
(2)运算符的相对次序不同;
(3)后缀表达式中已考虑了运算符的优先级,没有括号,只有操作数和运算符。
列:算术表达式求值 :参与运算的数据(即操作数)可以为整数或实数,运算符(即操作符)为+、-、、/(加、减、乘、除)等二元操作符,包括圆括号;
如 :
56+10-25=15或者 6+(5-4/2)*3=15
运算规则:先乘除,后加减,从左到右计算,先括号内,后括号外;
2、中缀表达式转换为后缀表达式
(1)存储结构
①后缀表达式队列:postQueue,用于存储逆波兰表达式(其实不用队列排序直接输出也行)
②操作符栈:opStack,对用户输入的操作符进行处理,用于存储运算符
(2)算法思想
从左向右依次读取算术表达式的元素X,分以下情况进行不同的处理:
1)如果X是操作数,直接入队
2)如果X是运算符,再分以下情况:
a)如果栈为空,直接入栈。
b)如果X==”(“,直接入栈。
c)如果X==”)“,则将栈里的元素逐个出栈,并入队到后缀表达式队列中,直到第一个配对的”(”出栈。(注:“(”和“)”都不 入队)
d)如果是其他操作符(+ - * /),则和栈顶元素进行比较优先级。 如果栈顶元素的优先级大于等于X,则出栈并把栈中弹出的元素入队,直到栈顶元素的优先级小于X或者栈为空。弹出完这些元素后,才将遇到的操作符压入到栈中。
3)最后将栈中剩余的操作符全部入队。
3、后缀表达式的计算
(1)需要用到一个结果栈Res_Stack :用于存放计算的中间过程的值和最终结果
(2)算法思想
首先准备一个栈Res_Stack.
1)从左开始向右遍历后缀表达式的元素。
2)如果取到的元素是操作数,直接入栈Res_Stack,如果是运算符,从栈中弹出2个数进行运算,然后把运算结果入栈
3) 当遍历完后缀表达式时,计算结果就保存在栈里了。
设计思路
1、首先是创建一个MFC项目用来显示基本的窗口
2、之后创建相应的“.h” 头文件
3、接着创建“.cpp”源文件用于实现计算器功能及可视化
具体实现
1、首先是如何通过MFC显示一个窗口
2、“.h”文件的创建
// xch-CakulatorDlg.h: 头文件
//
#pragma once
//运算符
enum Calcuator_Flag
{
FLAG_ADD,
FLAG_SUB,
FLAG_MUL,
FLAG_DIV,
};
// CxchCakulatorDlg 对话框
class CxchCakulatorDlg : public CDialogEx
{
// 构造
public:
CxchCakulatorDlg(CWnd* pParent = nullptr); // 标准构造函数
void SaveFirstValue(); //保存第一个输入值
void Calculator(); //计算
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_XCHCAKULATOR_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
private:
CString str; //编辑框1内的字符
CString str1; //编辑框2内的字符
CString str2;
CString str3;
CString str4;
CString str5;
CString str6;
CString TempStr;
double NUM1; //输入两个数
double NUM2;
Calcuator_Flag Flag;
public:
afx_msg void OnBnClickedButtonnum0();
afx_msg void OnBnClickedButtonnum1();
afx_msg void OnBnClickedButtonnum2();
afx_msg void OnBnClickedButtonnum3();
afx_msg void OnBnClickedButtonnum4();
afx_msg void OnBnClickedButtonnum5();
afx_msg void OnBnClickedButtonnum6();
afx_msg void OnBnClickedButtonnum7();
afx_msg void OnBnClickedButtonnum8();
afx_msg void OnBnClickedButtonnum9();
afx_msg void OnBnClickedButtonback();
afx_msg void OnBnClickedButtonclear();
afx_msg void OnBnClickedButtonpoint();
afx_msg void OnBnClickedButtonadd();
afx_msg void OnBnClickedButtonsub();
afx_msg void OnBnClickedButtonmul();
afx_msg void OnBnClickedButtondiv();
afx_msg void OnBnClickedButtonequal();
afx_msg void OnBnClickedClearall();
};
3、“.c”文件的创建
//
//
// xch-CakulatorDlg.cpp: 实现文件
//
#include "pch.h"
#include "framework.h"
#include "xch-Cakulator.h"
#include "xch-CakulatorDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CxchCakulatorDlg 对话框
CxchCakulatorDlg::CxchCakulatorDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_XCHCAKULATOR_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CxchCakulatorDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT1, str);
DDX_Text(pDX, IDC_EDIT2, str1);
DDX_Text(pDX, IDC_EDIT3, str2);
DDX_Text(pDX, IDC_EDIT4, str3);
DDX_Text(pDX, IDC_EDIT5, str4);
DDX_Text(pDX, IDC_EDIT6, str5);
DDX_Text(pDX, IDC_EDIT7, str6);
}
BEGIN_MESSAGE_MAP(CxchCakulatorDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTONNum0, &CxchCakulatorDlg::OnBnClickedButtonnum0)
ON_BN_CLICKED(IDC_BUTTONNum1, &CxchCakulatorDlg::OnBnClickedButtonnum1)
ON_BN_CLICKED(IDC_BUTTONNum2, &CxchCakulatorDlg::OnBnClickedButtonnum2)
ON_BN_CLICKED(IDC_BUTTONNum3, &CxchCakulatorDlg::OnBnClickedButtonnum3)
ON_BN_CLICKED(IDC_BUTTONNum4, &CxchCakulatorDlg::OnBnClickedButtonnum4)
ON_BN_CLICKED(IDC_BUTTONNum5, &CxchCakulatorDlg::OnBnClickedButtonnum5)
ON_BN_CLICKED(IDC_BUTTONNum6, &CxchCakulatorDlg::OnBnClickedButtonnum6)
ON_BN_CLICKED(IDC_BUTTONNum7, &CxchCakulatorDlg::OnBnClickedButtonnum7)
ON_BN_CLICKED(IDC_BUTTONNum8, &CxchCakulatorDlg::OnBnClickedButtonnum8)
ON_BN_CLICKED(IDC_BUTTONNum9, &CxchCakulatorDlg::OnBnClickedButtonnum9)
ON_BN_CLICKED(IDC_BUTTONBack, &CxchCakulatorDlg::OnBnClickedButtonback)
ON_BN_CLICKED(IDC_BUTTONClear, &CxchCakulatorDlg::OnBnClickedButtonclear)
ON_BN_CLICKED(IDC_BUTTONPoint, &CxchCakulatorDlg::OnBnClickedButtonpoint)
ON_BN_CLICKED(IDC_BUTTONAdd, &CxchCakulatorDlg::OnBnClickedButtonadd)
ON_BN_CLICKED(IDC_BUTTONSub, &CxchCakulatorDlg::OnBnClickedButtonsub)
ON_BN_CLICKED(IDC_BUTTONMul, &CxchCakulatorDlg::OnBnClickedButtonmul)
ON_BN_CLICKED(IDC_BUTTONDiv, &CxchCakulatorDlg::OnBnClickedButtondiv)
ON_BN_CLICKED(IDC_BUTTONEqual, &CxchCakulatorDlg::OnBnClickedButtonequal)
ON_BN_CLICKED(ClearAll, &CxchCakulatorDlg::OnBnClickedClearall)
END_MESSAGE_MAP()
// CxchCakulatorDlg 消息处理程序
BOOL CxchCakulatorDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CxchCakulatorDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CxchCakulatorDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CxchCakulatorDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CxchCakulatorDlg::SaveFirstValue()
{
UpdateData(TRUE);
NUM1 = _wtof(str);
TempStr = str;
str = L"";
UpdateData(FALSE);
}
void CxchCakulatorDlg::Calculator()
{
UpdateData(TRUE);
NUM2 = _wtof(str);
double result = 0.0f;
switch (Flag)
{
case FLAG_ADD:
result = NUM1 + NUM2;
TempStr = TempStr + _T("+") + str + _T("=");
break;
case FLAG_SUB:
result = NUM1 - NUM2;
TempStr = TempStr + _T("-") + str + _T("=");
break;
case FLAG_MUL:
result = NUM1 * NUM2;
TempStr = TempStr + _T("*") + str + _T("=");
break;
case FLAG_DIV:
if (NUM2 == 0.0f)
{
result = NUM1;
TempStr = _T("--除数不能为0!!!--");
}
else
{
result = NUM1 / NUM2;
TempStr = TempStr + _T("/") + str + _T("=");
break;
}
default:
break;
}
//如果浮点数是个整数,就显示为整数
if (result - int(result) <= 1e-5)
{
str.Format(L"%d", (int)result);
}
else
{
str.Format(L"%f", result);
}
TempStr += str;
str5 = str4;
str4 = str3;
str3 = str2;
str2 = str1;
str1 = TempStr;
UpdateData(FALSE);
NUM1 = result;
NUM2 = 0.0f;
}
void CxchCakulatorDlg::OnBnClickedButtonnum0()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"0";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonnum1()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"1";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonnum2()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"2";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonnum3()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"3";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonnum4()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"4";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonnum5()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"5";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonnum6()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"6";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonnum7()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"7";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonnum8()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"8";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonnum9()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str += L"9";
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonback() //Back
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
if (!str.IsEmpty())
{
str = str.Left(str.GetLength() - 1);
}
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonclear() //Clear
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str = L"";
NUM1 = 0.0f;
NUM2 = 0.0f;
Flag = FLAG_ADD;
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonpoint() //小数点
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
//如果没有小数点,则添加小数点;如果有,则会略本次的小数点,保证最多1个
if (-1 == str.Find(L'.'))
{
str += L".";
}
UpdateData(FALSE);
}
void CxchCakulatorDlg::OnBnClickedButtonadd() //加
{
// TODO: 在此添加控件通知处理程序代码
SaveFirstValue();
Flag = FLAG_ADD;
}
void CxchCakulatorDlg::OnBnClickedButtonsub() //减
{
// TODO: 在此添加控件通知处理程序代码
SaveFirstValue();
Flag = FLAG_SUB;
}
void CxchCakulatorDlg::OnBnClickedButtonmul() //乘
{
// TODO: 在此添加控件通知处理程序代码
SaveFirstValue();
Flag = FLAG_MUL;
}
void CxchCakulatorDlg::OnBnClickedButtondiv() //除
{
// TODO: 在此添加控件通知处理程序代码
SaveFirstValue();
Flag = FLAG_DIV;
}
void CxchCakulatorDlg::OnBnClickedButtonequal() //等于
{
// TODO: 在此添加控件通知处理程序代码
Calculator();
}
void CxchCakulatorDlg::OnBnClickedClearall()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
str = L"";
str1 = L"";
str2 = L"";
str3 = L"";
str4 = L"";
str5 = L"";
NUM1 = 0.0f;
NUM2 = 0.0f;
Flag = FLAG_SUB;
UpdateData(FALSE);
}
总结
1、结果展示:
新人一枚,主要还是看B站学的,但也稍微有了一些新的认识吧!希望后面会有更大的进步!