MFC實現的計算器
最近補了一下UI方面的,感覺還好,同時發現算法方面真的還有很多方面要補充的,希望最近一段忙完後開始學習算法,大一錯過了ACM是自己的遺憾,希望接下來一段時間補回來
代碼已上傳:https://github.com/mcc321/UI/tree/master/MFC/test2/test2
其實寫這個計算器最難的倒不是新學習的MFC,感覺MFC其實還是挺簡單的,VS已經做的很完善了,也許是自己學的還很淺,但真的覺得mfc短時間入門其實還是可以做到的。
下面簡單介紹一下MFC
// test2Dlg.h: 頭文件
//
#pragma once
#include <string>
#include <vector>
using namespace std;
// Ctest2Dlg 對話框
class Ctest2Dlg : public CDialogEx
{
// 構造
public:
Ctest2Dlg(CWnd* pParent = nullptr); // 標準構造函數
// 對話框數據
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_TEST2_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()
public:
CButton m_modulo;
CButton m_reset;
CButton m_delete;
CButton m_divide;
CButton m_7;
CButton m_8;
CButton m_9;
CButton m_multiply;
CButton m_4;
CButton m_5;
CButton m_6;
CButton m_sub;
CButton m_1;
CButton m_2;
CButton m_3;
CButton m_add;
CButton m_0;
CButton m_point;
CButton m_equal;
CString m_display;
CString m_result;
afx_msg void OnBnClickedButton1();
afx_msg void OnBnClickedButton2();
afx_msg void OnBnClickedButton3();
afx_msg void OnBnClickedButton4();
afx_msg void OnBnClickedButton5();
afx_msg void OnBnClickedButton6();
afx_msg void OnBnClickedButton7();
afx_msg void OnBnClickedButton8();
afx_msg void OnBnClickedButton9();
afx_msg void OnBnClickedButton10();
afx_msg void OnBnClickedButton11();
afx_msg void OnBnClickedButton12();
afx_msg void OnBnClickedButton13();
afx_msg void OnBnClickedButton14();
afx_msg void OnBnClickedButton15();
afx_msg void OnBnClickedButton16();
afx_msg void OnBnClickedButton18();
afx_msg void OnBnClickedButton19();
afx_msg void OnBnClickedButton20();
afx_msg void OnEnChangeEdit1();
afx_msg void OnEnChangeEdit2();
void calculator();
struct Result split(string);
afx_msg void OnBnClickedButton17();
};
這裏簡單介紹一下,這些以afx_msg函數都是自動生成的,對應於每個按鍵點擊時的響應事件,當然還有其它事件。CButton定義的變量對應每個按鍵變量,下面自己還定義了兩個處理函數。
// test2Dlg.cpp: 實現文件
//
# include <iostream>
# include "pch.h"
# include "framework.h"
# include "test2.h"
# include "test2Dlg.h"
# include "afxdialogex.h"
# include "stack"
# include "tuple"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
using namespace std;
// 用於應用程序“關於”菜單項的 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()
// Ctest2Dlg 對話框
Ctest2Dlg::Ctest2Dlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_TEST2_DIALOG, pParent)
, m_display(_T(""))
, m_result(_T(""))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void Ctest2Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_BUTTON1, m_modulo);
DDX_Control(pDX, IDC_BUTTON2, m_reset);
DDX_Control(pDX, IDC_BUTTON3, m_delete);
DDX_Control(pDX, IDC_BUTTON4, m_divide);
DDX_Control(pDX, IDC_BUTTON5, m_7);
DDX_Control(pDX, IDC_BUTTON6, m_8);
DDX_Control(pDX, IDC_BUTTON7, m_9);
DDX_Control(pDX, IDC_BUTTON8, m_multiply);
DDX_Control(pDX, IDC_BUTTON9, m_4);
DDX_Control(pDX, IDC_BUTTON10, m_5);
DDX_Control(pDX, IDC_BUTTON11, m_6);
DDX_Control(pDX, IDC_BUTTON12, m_sub);
DDX_Control(pDX, IDC_BUTTON13, m_1);
DDX_Control(pDX, IDC_BUTTON14, m_2);
DDX_Control(pDX, IDC_BUTTON15, m_3);
DDX_Control(pDX, IDC_BUTTON16, m_add);
DDX_Control(pDX, IDC_BUTTON18, m_0);
DDX_Control(pDX, IDC_BUTTON19, m_point);
DDX_Control(pDX, IDC_BUTTON20, m_equal);
DDX_Text(pDX, IDC_EDIT1, m_display);
DDX_Text(pDX, IDC_EDIT2, m_result);
}
BEGIN_MESSAGE_MAP(Ctest2Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &Ctest2Dlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &Ctest2Dlg::OnBnClickedButton2)
ON_BN_CLICKED(IDC_BUTTON3, &Ctest2Dlg::OnBnClickedButton3)
ON_BN_CLICKED(IDC_BUTTON4, &Ctest2Dlg::OnBnClickedButton4)
ON_BN_CLICKED(IDC_BUTTON5, &Ctest2Dlg::OnBnClickedButton5)
ON_BN_CLICKED(IDC_BUTTON6, &Ctest2Dlg::OnBnClickedButton6)
ON_BN_CLICKED(IDC_BUTTON7, &Ctest2Dlg::OnBnClickedButton7)
ON_BN_CLICKED(IDC_BUTTON8, &Ctest2Dlg::OnBnClickedButton8)
ON_BN_CLICKED(IDC_BUTTON9, &Ctest2Dlg::OnBnClickedButton9)
ON_BN_CLICKED(IDC_BUTTON10, &Ctest2Dlg::OnBnClickedButton10)
ON_BN_CLICKED(IDC_BUTTON11, &Ctest2Dlg::OnBnClickedButton11)
ON_BN_CLICKED(IDC_BUTTON12, &Ctest2Dlg::OnBnClickedButton12)
ON_BN_CLICKED(IDC_BUTTON13, &Ctest2Dlg::OnBnClickedButton13)
ON_BN_CLICKED(IDC_BUTTON14, &Ctest2Dlg::OnBnClickedButton14)
ON_BN_CLICKED(IDC_BUTTON15, &Ctest2Dlg::OnBnClickedButton15)
ON_BN_CLICKED(IDC_BUTTON16, &Ctest2Dlg::OnBnClickedButton16)
ON_BN_CLICKED(IDC_BUTTON18, &Ctest2Dlg::OnBnClickedButton18)
ON_BN_CLICKED(IDC_BUTTON19, &Ctest2Dlg::OnBnClickedButton19)
ON_BN_CLICKED(IDC_BUTTON20, &Ctest2Dlg::OnBnClickedButton20)
ON_EN_CHANGE(IDC_EDIT1, &Ctest2Dlg::OnEnChangeEdit1)
ON_EN_CHANGE(IDC_EDIT2, &Ctest2Dlg::OnEnChangeEdit2)
ON_BN_CLICKED(IDC_BUTTON17, &Ctest2Dlg::OnBnClickedButton17)
END_MESSAGE_MAP()
// Ctest2Dlg 消息處理程序
BOOL Ctest2Dlg::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 Ctest2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向對話框添加最小化按鈕,則需要下面的代碼
// 來繪製該圖標。 對於使用文檔/視圖模型的 MFC 應用程序,
// 這將由框架自動完成。
void Ctest2Dlg::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 Ctest2Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void Ctest2Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '%';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_result = L"";
m_display = m_result;
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
if (!m_display.IsEmpty()) {
m_display = m_display.Left(m_display.GetLength() - 1);
}
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '/';
UpdateData(TRUE);
}
void Ctest2Dlg::OnBnClickedButton5()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '7';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton6()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '8';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton7()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '9';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton8()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '*';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton9()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '4';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton10()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '5';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton11()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '6';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton12()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '-';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton13()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '1';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton14()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '2';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton15()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '3';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton16()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '+';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton18()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_display += '0';
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton19()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
if (m_result.Find('.') == -1) {
m_display += '.';
}
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton20()
{
// TODO: 在此添加控件通知處理程序代碼
calculator();
}
void Ctest2Dlg::OnEnChangeEdit1()
{
// TODO: 如果該控件是 RICHEDIT 控件,它將不
// 發送此通知,除非重寫 CDialogEx::OnInitDialog()
// 函數並調用 CRichEditCtrl().SetEventMask(),
// 同時將 ENM_CHANGE 標誌“或”運算到掩碼中。
// TODO: 在此添加控件通知處理程序代碼
}
void Ctest2Dlg::OnEnChangeEdit2()
{
// TODO: 如果該控件是 RICHEDIT 控件,它將不
// 發送此通知,除非重寫 CDialogEx::OnInitDialog()
// 函數並調用 CRichEditCtrl().SetEventMask(),
// 同時將 ENM_CHANGE 標誌“或”運算到掩碼中。
// TODO: 在此添加控件通知處理程序代碼
}
struct Result {
vector<char> flag;
vector<float> str;
};
struct R {
int a;
int b;
};
struct R find_s(vector<char> s) {
struct R r;
r.a = -1;
r.b = -1;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '*' || s[i] == '/' || s[i] == '%' || s[i] == '+' || s[i] == '-') {
r.a += 1;
if (s[i] == '*')
{
r.b = 1;
return r;
}
if (s[i] == '/')
{
r.b = 2;
return r;
}
if (s[i] == '%')
{
r.b = 3;
return r;
}
}
}
r.a = 0;
r.b = 0;
return r;
}
struct R find_i(vector<char> s) {
struct R r;
r.a = -1;
r.b = -1;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '+' || s[i] == '-') {
r.a += 1;
if (s[i] == '+')
{
r.b = 5;
return r;
}
if (s[i] == '-')
{
r.b = 6;
return r;
}
}
}
r.a = 0;
r.b = 4;
return r;
}
struct Result Ctest2Dlg::split(string s) {
bool t = 0;
vector<char> flag;
string tmp;
vector<float> str;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i] == '%') {
if (!tmp.size()) {
if (s[i - 1] == '*' || s[i - 1] == '/' || s[i - 1] == '%')
t = 1;
else
str.push_back(0);
}
else {
if (t)
{
str.push_back(-stof(tmp));
t = 0;
}
else {
str.push_back(stof(tmp));
}
tmp = "";
}
if (!t)
flag.push_back(s[i]);
}
else {
tmp = tmp + s[i];
}
}
str.push_back(stof(tmp));
struct Result result;
result.flag = flag;
result.str = str;
return result;
}
void Ctest2Dlg::calculator()
{
UpdateData(TRUE); //取控件中的值
string str = CT2A(m_display.GetString());
struct Result result = split(str);
while (result.flag.size()) {
while (1) {
struct R r = find_s(result.flag);
float n;
if (r.b == 1)
n = result.str[r.a] * result.str[r.a + 1];
if (r.b == 2)
n = result.str[r.a] / result.str[r.a + 1];
if (r.b == 3)
n = (int)result.str[r.a] % (int)result.str[r.a + 1];
if (r.b == 0)
break;
vector<float>::iterator it1 = result.str.begin() + r.a;
vector<char>::iterator it2 = result.flag.begin() + r.a;
result.str[r.a] = n;
result.str.erase(it1 + 1);
result.flag.erase(it2);
}
while (find_s(result.flag).b != 4) {
struct R r = find_i(result.flag);
float n;
if (r.b == 5)
n = result.str[r.a] + result.str[r.a + 1];
if (r.b == 6)
n = result.str[r.a] - result.str[r.a + 1];
if (r.b == 4)
break;
vector<float>::iterator it1 = result.str.begin() + r.a;
vector<char>::iterator it2 = result.flag.begin() + r.a;
result.str[r.a] = n;
result.str.erase(it1 + 1);
result.flag.erase(it2);
}
}
CA2T szr(to_string(result.str[0]).c_str());
m_result = (LPCTSTR)szr;
UpdateData(FALSE);
}
void Ctest2Dlg::OnBnClickedButton17()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
m_result = "GL";
m_display = "Designed by mcc";
UpdateData(FALSE);
}
其實這裏核心的算法只有一個其它的都是收集相關的按鍵信息,也是唯一的難點,那就是根據獲得的一串字符得到一個數字。
算法介紹
下面詳細介紹一下這個算法
- 拆分字符串,這裏拆分有一定的規則,考慮到數字對應以及語法的特殊性,這裏做了幾個簡單的處理,如果遇到兩個相連的操作符如:
/-
%-2
*-2
等需要補充不同的項,下面是拆分函數
struct Result {
vector<char> flag;
vector<float> str;
};
struct Result Ctest2Dlg::split(string s) {
bool t = 0;
vector<char> flag;
string tmp;
vector<float> str;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i] == '%') {
if (!tmp.size()) {
if (s[i - 1] == '*' || s[i - 1] == '/' || s[i - 1] == '%')
t = 1;
else
str.push_back(0);
}
else {
if (t)
{
str.push_back(-stof(tmp));
t = 0;
}
else {
str.push_back(stof(tmp));
}
tmp = "";
}
if (!t)
flag.push_back(s[i]);
}
else {
tmp = tmp + s[i];
}
}
str.push_back(stof(tmp));
struct Result result;
result.flag = flag;
result.str = str;
return result;
}
這裏定義了一個結構體,vector用來記錄符號,vector用來記錄數字,當然代碼沒有經過優化和詳細的設置,有很多錯誤情況不能進行處理,經不起暴力測試,只能處理正常輸入
- 優先級處理
while (result.flag.size()) {
while (1) {
struct R r = find_s(result.flag);
float n;
if (r.b == 1)
n = result.str[r.a] * result.str[r.a + 1];
if (r.b == 2)
n = result.str[r.a] / result.str[r.a + 1];
if (r.b == 3)
n = (int)result.str[r.a] % (int)result.str[r.a + 1];
if (r.b == 0)
break;
vector<float>::iterator it1 = result.str.begin() + r.a;
vector<char>::iterator it2 = result.flag.begin() + r.a;
result.str[r.a] = n;
result.str.erase(it1 + 1);
result.flag.erase(it2);
}
while (find_s(result.flag).b != 4) {
struct R r = find_i(result.flag);
float n;
if (r.b == 5)
n = result.str[r.a] + result.str[r.a + 1];
if (r.b == 6)
n = result.str[r.a] - result.str[r.a + 1];
if (r.b == 4)
break;
vector<float>::iterator it1 = result.str.begin() + r.a;
vector<char>::iterator it2 = result.flag.begin() + r.a;
result.str[r.a] = n;
result.str.erase(it1 + 1);
result.flag.erase(it2);
}
}
處理步驟是首先探測result.flag中是否存在* / %
,優先進行處理,然後在處理+ -
,每進行一輪處理便從flag中移除一個符號同時從str移除一個數字和修改一個數字,最後容器會只剩下一個元素,這個元素就是計算結果。
最終效果
雜談
今天和家人聊起了關於學習的問題,挺有感觸的,家裏說報個補習班,但自己覺得關於學習真的沒什麼可以勉強的,想學的時候自然會去學,而且樂此不疲,強迫的學習真的很低效,即使是憑藉自己的自制力強迫自己去學,就拿最近班上上課各種複習三級四級的,我的感受是沒用,自己當時的感觸是完全對學到的沒有興趣,只是想拿個證書(當時也沒想到保研什麼的),學完後,草草的考試,也以意外的過了,但事後回憶覺得真的是很浪費,浪費考試費同時也是浪費自己的時間,後來自己遇到網絡問題又重新學習na,np,才慢慢地對網絡有了一些認識。有時候就是覺得興趣到了,自然會主動去學習,不必執着於加分證書什麼的,這些給人的感受是很雞肋的,拿不到覺得很牛,拿到了覺得沒什麼用。有時候就是覺得很多事應該是自己主動嘗試去探索,發現自己感興趣的方面,然後好好學,不一定是一個方面,但一定願意爲它投入時間,且不覺得勉強,