ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

VC6.0 MFC计算器开发全指南:从对话框资源到消息映射与状态机实现

VC6.0 MFC计算器开发全指南:从对话框资源到消息映射与状态机实现 简介面向初学Windows GUI编程的人群这是一份可直接运行的VC6.0与MFC计算器工程源码包展示了利用对话框类、按钮与静态文本框控件构建桌面计算器的完整流程并配有MFC说明文档适合课程设计或自学入门。压缩包共31个文件包含源码头文件(h/cpp)、项目工程文件(dsw/dsp/rc)、位图与图标素材(bmp/ico等)以及说明文档(doc)整体约106KB结构精简便于逐项对照阅读。已有383人学习下载。通过阅读事件处理、消息映射和CString数值转换等关键代码学习者能快速复现数字按钮、运算符响应与结果显示逻辑也可作为熟悉MFC框架、练习调试与界面优化的良好起点。1. 一个 mfc 计算器值不值得折腾先看清 VC6.0 这套栈的边界如果你在学校里做完课程设计就要收手VC6.0 MFC 计算器是性价比最高的一个题工程小、界面能看、逻辑也不浅。对话框资源、控件消息映射、CString 转换、浮点精度一圈走完MFC 主干就都在手里了。哪怕手边只剩 vc6.0-mfc.rar 这样一个文件名目标也应该是自己能从头把工程点出来而不是解开包拿走别人的代码。技术栈边界得先讲清楚。VC6.0 是 1998 年的 IDE在 Win10/11 上装完大概率会遇到 no compile tool、调试器连不上这类环境问题MFC 本质也只是把 Win32 包了一层 C 类库新项目没人会主动选它。所以这个方向适合三类人要交 MFC 作业的学生、维护老系统里 MFC 小工具的人、想在 Win32 消息机制上补一课的人。它不是前沿方案但它是能长期自解释的地基代码。2. 拆一个 mfc 计算器的骨架.rc 资源、消息映射和界面刷新对话框程序一跑起来很多人以为控件是用代码一行行CreateWindow建出来的实际不是。VC6.0 的 AppWizard 生成的对话框工程里界面定义躺在 .rc 资源脚本文件中代码只是把这个资源加载出来。把资源、消息、刷新三条线理清整个计算器就算拆明白了一半。2.1 界面是资源脚本生成的.rc 文件里的坐标才是真相在 VC6.0 里双击资源视图里的对话框 ID会打开所见即所得的资源编辑器。你拖一个按钮、挪一个位置保存后改动全部写进 .rc 文本。工程重新编译时rc.exe把这个文本编译成二进制资源DoModal()再把它加载成窗口。一个最小计算器对话框的 .rc 片段长这样IDD_CALCULATOR_DIALOG DIALOGEX 0, 0, 174, 202 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION Calculator FONT 8, MS Sans Serif BEGIN EDITTEXT IDC_EDIT_DISPLAY, 10, 14, 120, 14, ES_AUTOHSCROLL PUSHBUTTON 7, IDC_BTN_7, 10, 40, 38, 25 PUSHBUTTON 8, IDC_BTN_8, 52, 40, 38, 25 PUSHBUTTON , IDC_BTN_ADD, 136, 40, 38, 25 PUSHBUTTON C, IDC_BTN_C, 136, 132, 38, 25 END每个控件一行依次是类型、显示文本、控件 ID、x、y、宽、高。坐标单位是对话框单位DLU不是像素所以别拿屏幕像素去推。调整位置用资源编辑器拖拽不推荐直接手改 .rc因为对话框单位的换算关系容易算错手改后对不齐还查不出原因。这里有个新手容易踩的坑类的enum { IDD IDD_CALCULATOR_DIALOG };这条语句把 C 类和资源 ID 绑在一起。如果你在 resource.h 里把IDD_CALCULATOR_DIALOG改名或误删编译报错还算好查最怕的是 IDE 里对话框正常打开、程序却弹出一个空白窗口多半就是 IDD 宏和资源对不上了。2.2 ON_BN_CLICKED 在做什么消息映射不是玄学是一张函数指针表Win32 里按钮被点击系统给父窗口发一条WM_COMMANDwParam 高字是通知码BN_CLICKED低字是控件 IDlParam 是按钮句柄。传统 SDK 程序要在窗口过程里写一大段 switch-case。MFC 的做法是用宏把控件 ID 通知码直接绑定到一个类成员函数这张表就是消息映射表BEGIN_MESSAGE_MAP(CCalculatorDlg, CDialog) ON_BN_CLICKED(IDC_BTN_0, OnBtn0) ON_BN_CLICKED(IDC_BTN_1, OnBtn1) ON_BN_CLICKED(IDC_BTN_ADD, OnBtnAdd) ON_BN_CLICKED(IDC_BTN_EQUAL, OnBtnEqual) ON_BN_CLICKED(IDC_BTN_C, OnBtnClear) END_MESSAGE_MAP()注意 VC6.0 生成的映射宏第二个参数是裸函数名OnBtn0不需要取地址符。这是老版本 MFC 的写法如果你从 VS2010 之后的项目里抄代码过来新写法是ON_BN_CLICKED(IDC_BTN_0, CCalculatorDlg::OnBtn0)直接粘贴到 VC6.0 会编译不过去掉和类限定符就好。ON_BN_CLICKED做的事本质是把这个控件 ID 和函数指针登记进一张表。消息到达时MFC 根据 ID 查表查到就调用对应函数。所以函数名写错、映射漏写、函数没实现三种情况表现完全不同后面第 5 章会专门讲链接错误的表现。2.3 显示刷新我不用 DDX而是用 SetDlgItemTextAppWizard 生成对话框类时DoDataExchange里默认是空的。很多人图方便给编辑框加一个 CString 成员变量再用DDX_Text绑定void CCalculatorDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_DISPLAY, m_strDisplay); }但计算器这种输入和输出共用同一个编辑框的程序DDX 反而容易坏事。你在按键处理里先UpdateData(TRUE)把控件文本读进成员变量改完再UpdateData(FALSE)写回这两次全量同步只要有一次中间状态不对显示就被覆盖成旧值。表现就是点了半天数字编辑框纹丝不动。我的做法是不绑定全部走SetDlgItemText/GetDlgItemText。写入用SetDlgItemText(IDC_EDIT_DISPLAY, m_strDisplay)读值用_tstof(...)或字符串操作完全绕开 DDX 的同步时机问题。两种方案的取舍方案优点缺点DDX_Text UpdateData变量和控件自动同步适合表单类界面全量读写中间态容易覆盖调试要关心 DoDataExchange 调用时机SetDlgItemText 直接写主动、可控、代码直观每次都要指定控件 ID代码稍啰嗦计算器这种只有一两个控件的程序代码啰嗦一点完全值得换来的是不用反复琢磨这个刷新是哪个 UpdateData 触发的。2.4 控件 ID 规划从 IDC_BTN_0 到 IDC_BTN_EQUAL 的命名规范VC6.0 默认给新按钮起名IDC_BUTTON1、IDC_BUTTON2排下来的 ID 毫无语义消息映射写多了根本分不清谁是谁。我一般按IDC_控件类型_含义的规则重命名控件ID用途显示框IDC_EDIT_DISPLAY显示当前输入和结果数字键 0-9IDC_BTN_0 到 IDC_BTN_9输入数字小数点IDC_BTN_POINT输入小数点加/减/乘/除IDC_BTN_ADD / IDC_BTN_SUB / IDC_BTN_MUL / IDC_BTN_DIV四则运算符等号IDC_BTN_EQUAL结算清零IDC_BTN_C全部复位清当前输入IDC_BTN_CE只清当前操作数这些 ID 定义在 resource.h 里由资源编辑器维护。改 ID 一定要在控件属性窗口改资源编辑器会自动同步到 resource.h不要手动去翻头文件改否则容易产生 ID 重复定义编译时报一堆resource.h 中的符号重复定义。3. 用 VC6.0 从零把计算器工程点出来AppWizard 选项与控件排布3.1 新建工程时三处关键选择错过一个后面都是坑启动 VC6.0 后File - New 切到 Projects 标签选 MFC AppWizard (exe)工程名填 Calculator。路径上不能有中文和空格否则后面资源编译器rc.exe经常报找不到文件这是老 IDE 最常见的中文路径毛病。向导前三步里需要拍板的实际就三处Step 1 选 Dialog based生成的是对话框程序而不是文档视图结构。Step 2 把 ActiveX Controls 的勾去掉。计算器用不到 ActiveX保留它会在 InitInstance 里多一次AfxEnableControlContainer()调用没什么影响但去掉后生成代码更干净。About box 保留。它会生成一个关于对话框后面想删随时删。向导生成的入口代码很短整个程序的生命周期就在InitInstance里BOOL CCalculatorApp::InitInstance() { CCalculatorDlg dlg; m_pMainWnd dlg; dlg.DoModal(); return FALSE; }CCalculatorApp是应用对象负责初始化 MFC 框架CCalculatorDlg才是真正干活的。DoModal()是一个阻塞调用弹出模态对话框后程序就停在这里直到用户关闭窗口才返回。return FALSE表示应用随后退出不会进入消息循环——对话框自己内部有消息循环这一点和 SDI/MDI 程序不同。生成的文件里CalculatorDlg.h和CalculatorDlg.cpp是主战场resource.h管控件 IDstdafx.h是预编译头。记住#include stdafx.h永远放在 .cpp 第一行这个顺序错了VC6.0 会报一堆莫名其妙的语法错误。3.2 控件排布与属性一个 Edit 加四行按钮的布局清单对话框模板生成后只有一个确定和取消按钮先删掉。从控件工具箱往对话框上放一个 Edit Box 和若干 Button布局参考第一行Edit 显示框拉宽占掉上部全部宽度。第二行7、8、9、/ 四个按钮。第三行4、5、6、* 四个按钮。第四行1、2、3、- 四个按钮。第五行0、.、、 四个按钮。显示框属性里把 Number 勾上它只影响输入法的弹窗限制对程序主动SetWindowText没有影响Read Only 不要勾否则用户没法选中复制结果。显示文本对齐方式选 Right这样数字从右往左显示更接近系统计算器。按钮属性里注意两点一是数字按钮的 Default Button 必须保持不勾否则回车会触发它和等号逻辑打架二是对话框的 Tab 顺序要让 Edit 排在最前。Tab 顺序用 CtrlD 在资源编辑器里点选调整否则对话框弹出来焦点落在某个按钮上按数字键只触发按钮快捷键而不是输入数字这是个非常容易忽略的体验问题。3.3 用 ClassWizard 生成消息处理函数而不是手写映射VC6.0 里生成消息处理函数的正规路径是 ClassWizardCtrlW 打开后Message Maps 页Class name 选 CCalculatorDlgObject IDs 列表点 IDC_BTN_7Messages 列表点 BN_CLICKED点 Add Function默认函数名 OnBtn7再点 Edit Code 跳到代码位置。生成的代码分三段分布在两个文件里。头文件// CalculatorDlg.h afx_msg void OnBtn7();实现文件的消息映射表里多一行同时多一个函数体// CalculatorDlg.cpp BEGIN_MESSAGE_MAP(CCalculatorDlg, CDialog) ON_BN_CLICKED(IDC_BTN_7, OnBtn7) END_MESSAGE_MAP() void CCalculatorDlg::OnBtn7() { // 这里写数字 7 的输入逻辑 }为什么强调用 ClassWizard 而不是手敲因为手敲容易三处不一致声明写的是 OnBtn7映射里写成 OnBtn7 但函数体没实现或者函数体写成了别的名字。三个位置只要不齐编译期不一定报错链接期必报 LNK2001到时候查起来比点几下鼠标麻烦得多。血泪经验先让 ClassWizard 把骨架生出来再往函数体里填业务代码。4. 计算器核心逻辑状态机、连续运算与浮点显示计算器的逻辑不复杂但连续按运算符和连续按等号这两个边界很容易写翻车。核心是用一个很小的状态机当前显示值、累积值、当前运算符、是否开始输入新数字。把四个状态量管住大部分问题就消失了。4.1 数字输入与 m_bNewNumber先想清楚新数字什么时候开始计算器有两种输入场景正常输入数字以及按完运算符后输入新数字。如果不用标志位区分会出现5 3输成5 35之类的结果。成员变量这样设计// CalculatorDlg.h 私有成员 CString m_strDisplay; // 显示框当前文本 double m_dAccumulator; // 已经累积的数值 char m_chOp; // 当前运算符0 表示没有 BOOL m_bNewNumber; // TRUE 表示下一次数字键要重新开始构造函数里初始化m_strDisplay _T(0)m_dAccumulator 0.0m_chOp 0m_bNewNumber TRUE。所有成员必须在构造函数里赋初值这条后面第 5 章还要再强调。数字键的处理统一走一个辅助函数void CCalculatorDlg::AppendDigit(int nDigit) { if (m_bNewNumber) { m_strDisplay _T(0); m_bNewNumber FALSE; } if (m_strDisplay _T(0) nDigit ! 0) { m_strDisplay.Format(_T(%d), nDigit); // 去掉前导 0 } else { m_strDisplay (TCHAR)(_T(0) nDigit); } SetDlgItemText(IDC_EDIT_DISPLAY, m_strDisplay); } void CCalculatorDlg::OnBtn0() { AppendDigit(0); } void CCalculatorDlg::OnBtn1() { AppendDigit(1); } // 2~9 同理逻辑要义有两个。第一个是新数字标志按完运算符后m_bNewNumber被置 TRUE此时按数字会先清掉旧显示而不是把新数字追加到旧数字后面。第二个是前导 0 的去处显示0时按 0 还是0按 1 会直接把0替换成1否则会出现01这种丑结果。(TCHAR)(_T(0) nDigit)是把整数 0~9 转成字符 0~9 的标准写法。如果项目定义了 UNICODETCHAR是 wchar_t这一句同样成立不会有类型截断问题。4.2 运算符键的两种时刻先结算旧操作再记录新操作运算符键按下时有两种情况如果之前已经有运算符并且用户正在输第二个数比如输完 5 按 再输 3 再按 这时应该先把 53 结算掉再记下新的 如果用户是连续按运算符比如 5 3这时不能重复结算只更新运算符即可。void CCalculatorDlg::OnBtnAdd() { if (m_chOp ! 0 !m_bNewNumber) { Execute(); // 旧运算先结算5 3 时把 53 算成 8 } else { m_dAccumulator _tstof(m_strDisplay); } m_chOp ; m_bNewNumber TRUE; }Execute()是真正做四则运算的地方void CCalculatorDlg::Execute() { double dRight _tstof(m_strDisplay); switch (m_chOp) { case : m_dAccumulator dRight; break; case -: m_dAccumulator - dRight; break; case *: m_dAccumulator * dRight; break; case /: if (dRight 0.0) { MessageBox(_T(除数不能为 0), _T(Calculator), MB_OK | MB_ICONERROR); return; } m_dAccumulator / dRight; break; default: m_dAccumulator dRight; break; } m_strDisplay.Format(_T(%.10g), m_dAccumulator); SetDlgItemText(IDC_EDIT_DISPLAY, m_strDisplay); }先解释Execute()里的两个细节。_tstof在 ANSI 构建下就是atof在 UNICODE 下是_wtof比直接写atof更稳。%.10g是显示精度的关键下节展开。除法里的dRight 0.0判断用户输入0时解析出来就是 0.0这种直接比较是可靠的如果你担心极小浮点数后面可以加阈值判断对计算器来说没必要。再回来看运算符分支的时序。走查一遍 5 3 按 5m_bNewNumber为 TRUE显示设为 5。按 m_chOp是 0走 elsem_dAccumulator被设为 5运算符记为 m_bNewNumber置 TRUE。按 3因为m_bNewNumber是 TRUE先重置显示为 0 再追加 3显示 3标志置 FALSE。再按 这时m_chOp ! 0且m_bNewNumber FALSE进入Execute()算出 538显示 8accumulator 变成 8运算符更新为 。这套状态机对连续按运算符同样自洽按 5 3 时第二次按 时m_bNewNumber还是 TRUE走 else 分支直接用当前显示值刷新 accumulator不会重复结算。4.3 等号、C 与 CE边界输入怎么防呆等号在状态机里是最简单的收口void CCalculatorDlg::OnBtnEqual() { if (m_chOp ! 0) { Execute(); m_chOp 0; m_bNewNumber TRUE; } }如果当前没有运算符等号直接忽略避免误触。如果想让按完等号再按等号重复上一次运算需要额外保存上一次的运算符和右操作数这里先不做第六章扩展里给思路。全清和清当前输入分开void CCalculatorDlg::OnBtnClear() // C彻底复位 { m_dAccumulator 0.0; m_chOp 0; m_bNewNumber TRUE; m_strDisplay _T(0); SetDlgItemText(IDC_EDIT_DISPLAY, m_strDisplay); } void CCalculatorDlg::OnBtnClearEntry() // CE只清当前输入保留累积 { m_bNewNumber TRUE; m_strDisplay _T(0); SetDlgItemText(IDC_EDIT_DISPLAY, m_strDisplay); }这两个键的差异容易被忽略。C 是全部推倒重来CE 是只清正在输入的这半截。走查 5 3 时按 CE 再按 4 再按等号CE 把显示清成 0但 accumulator 仍是 5运算符仍是 接着按 4 显示 4等号结算 549。这个语义和 Windows 自带计算器一致做课程设计时老师说计算器要做得像样这个小功能很加印象分。4.4 显示精度%.10g 与浮点误差的取舍浮点数运算天生有误差0.10.2 的 double 结果其实是 0.30000000000000004。如果你用%.2f或直接把 double 塞给 CString显示就会翻车。常见格式化策略格式行为适用场景%g自动去掉尾零最多保留 6 位有效数字结果不超过 6 位时很干净超过会切精度%.10g保留最多 10 位有效数字尾零自动去掉计算器通用推荐%f固定小数位尾零不去做账要固定小数点时用Execute()里用的%.10g会把 0.10.2 显示成 0.3因为 10 位有效数字内误差被截掉了1÷3 显示 0.3333333333位数够看且不会一长串 3 占满屏幕。代价是超大数会切换成科学计数法比如 1e15 以上会显示成 1e15这是可接受的因为普通计算器场景很少用到这么大的数。如果想让显示更接近 Windows 10 计算器可以把%.10g换成%.17gdouble 全精度展示但 0.10.2 就会露出 0.30000000000000004 的马脚。所以 10 位是个实用折中点。5. VC6.0 常见问题与避坑排查从安装到运行的五次翻车现场5.1 no compile tool装完 IDE 却编译不了现象VC6.0 安装后打开新建工程Build 菜单全灰编译时提示 no compile tool 或者找不到编译器。原因VC6.0 的安装器是组件式安装如果安装过程中没勾选 VC 6.0 编译器组件或者安装过程被安全软件静默拦截了部分组件就会出现IDE 在但工具链不在的半残状态。这个问题在 Win10/11 上尤其常见安装包解压不完整也会触发。解决控制面板 - 添加/删除程序 - 找到 Microsoft Visual C 6.0 - 更改 - 修复安装器会补装缺失组件。如果系统是新版本 Windows右键 VC6.0 快捷方式属性里设成以管理员身份运行兼容模式选 Windows XP (SP3)。注意装完补丁还要重开一次 IDEBuild 菜单才恢复。5.2 LNK2001: unresolved external symbol多半是名字不一致现象编译能过链接时报类似LNK2001: unresolved external symbol void __thiscall CCalculatorDlg::OnBtnAdd(void)你说函数明明写了的。原因链接器找不到的实现通常三处不一致头文件声明了afx_msg void OnBtnAdd()实现文件里函数体却叫OnButtonAdd()或者消息映射表里写的是OnBtnAdd函数体实现的是OnBtnAdd但函数体根本没写也可能函数体写了被#if 0注释掉了。解决把测试片段放进实现文件对照头文件的声明一个个点名// CalculatorDlg.cpp void CCalculatorDlg::OnBtnAdd() { // 随便写一句先验证链接 int x 1; }如果链接错误消失说明是函数体缺失如果还在去消息映射表看函数名有没有写错。最省事的办法是删掉手写内容重新用 ClassWizard 生成一遍让它把三处一次性补齐。5.3 界面中文全变乱码ANSI 工程里混进 UTF-8 的后果现象按钮文本显示成 ??? 或者一组方框乱码对话框标题也是乱的但代码里明明写的是中文。原因VC6.0 默认按 ANSI 代码页编译资源中文系统上就是 GBK/GB2312。如果你的 .rc 文件或者 C 源文件被 IDE 或编辑器存成了 UTF-8 编码中文字符按 UTF-8 解释后和 GBK 对不上显示自然全乱。解决两个层面。一是源文件统一用 ANSI 编码保存VC6.0 自带的编辑器默认就是 ANSI别用 VS Code 或记事本转存成 UTF-8。二是界面文本尽量用符号和英文计算器按钮用 MC、MR、C、CE 这类符号天然避雷。如果非要显示中文确认整个工程的源文件和 .rc 都是 ANSI 编码并且系统区域设置里的代码页是 936。5.4 Debug 正常 Release 崩溃未初始化变量的两种面孔现象Debug 版点按钮反复运算都正常换成 Release 构建一按运算符就崩溃或算出随机数。原因Debug 版有调试堆未初始化的成员变量往往被零填充掩盖了问题Release 版变量初值是栈上的残留数据m_chOp可能是 0xCC 或别的负值switch进了 default 分支还好要是拿去做数组下标就崩了。解决检查所有成员变量是否在构造函数里赋了初值。计算器里最容易漏的是m_chOp和m_dAccumulator漏一个Release 下第一次按等号就可能进错误分支。给一个小技巧构造函数里统一写m_dAccumulator 0.0; m_chOp 0; m_bNewNumber TRUE; m_strDisplay _T(0);养成所有成员变量都在构造函数里显式初始化、绝不在声明处靠编译器默认值的习惯这个坑在 VC6.0 时代几乎人人踩过。5.5 Edit 控件不响应键盘输入焦点根本没给它现象对话框一弹出直接在编辑框里敲键盘没反应但鼠标点击编辑框后又能输了或者按数字键永远没数字出现。原因对话框初始焦点不在 Edit 控件上。模态对话框弹出后焦点停在 Tab 顺序里最靠前的可聚焦控件上如果你没调 Tab 顺序焦点可能落在某个按钮上用户敲数字触发的是按钮自然没有输入。解决在OnInitDialog里显式把焦点交给编辑框BOOL CCalculatorDlg::OnInitDialog() { CDialog::OnInitDialog(); GotoDlgCtrl(GetDlgItem(IDC_EDIT_DISPLAY)); return TRUE; }GotoDlgCtrl会把焦点移动到指定控件GetDlgItem(IDC_EDIT_DISPLAY)先拿到控件句柄。这样对话框一弹出光标就在输入框里数字直接落到显示区。另外一个细节OnInitDialog的返回值如果返回 TRUE系统会采用你设置的焦点返回 FALSE 则保持默认所以这里要写 TRUE。6. 把计算器做扎实结构化验证与三个小扩展6.1 用一个自检函数做回归不靠肉眼逐次点按钮功能写完最怕改一处崩三处。计算器这种消息驱动的小程序可以用SendMessage模拟点击把整个逻辑链完整跑一遍void CCalculatorDlg::RunSelfTest() { // 用 WM_COMMAND 模拟真实点击走完整的消息映射路径 SendMessage(WM_COMMAND, MAKEWPARAM(IDC_BTN_C, BN_CLICKED)); SendMessage(WM_COMMAND, MAKEWPARAM(IDC_BTN_1, BN_CLICKED)); SendMessage(WM_COMMAND, MAKEWPARAM(IDC_BTN_2, BN_CLICKED)); SendMessage(WM_COMMAND, MAKEWPARAM(IDC_BTN_ADD, BN_CLICKED)); SendMessage(WM_COMMAND, MAKEWPARAM(IDC_BTN_7, BN_CLICKED)); SendMessage(WM_COMMAND, MAKEWPARAM(IDC_BTN_EQUAL, BN_CLICKED)); CString strResult; GetDlgItemText(IDC_EDIT_DISPLAY, strResult); if (strResult ! _T(19)) AfxMessageBox(_T(自检失败: 127 期望 19实际 ) strResult); }MAKEWPARAM(IDC_BTN_C, BN_CLICKED)拼出的 wParam 和真实点击完全一致等于绕过了鼠标设备层直接触发同一套消息分发。这个函数可以挂在某个隐藏按钮上也可以临时在OnInitDialog末尾调用一次。以后改逻辑跑一遍这个自检比肉眼点二十遍按钮靠谱得多。6.2 三个值得顺手加的扩展第一个是连续按等号重复上一运算。在Execute()里把右操作数存下来等号后如果m_chOp 0但保存的运算符还在就再执行一次。系统计算器按完 3 结果是 9、27就是这套逻辑。第二个是键盘输入。重写对话框的PreTranslateMessage拦截WM_KEYDOWN把 VK_0 到 VK_9 映射到AppendDigit小键盘和主键盘的键码略有差异分别处理。这个扩展加上后计算器才真正能用。第三个是运算优先级。现在的状态机是严格从左到右乘除不优先于加减。要做先乘除后加减得把输入解析成中缀表达式再转后缀求值数据结构上需要两个栈。计算器功能到这一步就从小作业变成小系统了但核心的按钮、消息映射、显示刷新都不用改这也验证了这套骨架的可扩展性。这些年我养成的习惯是任何程序都留一个能自动跑的自检入口哪怕只是个隐藏函数改完代码先跑它再手工点。计算器这代练完MFC 的骨架你就拿下了以后看到 vc6.0 相关的老工程也不会再对着.dsw文件发怵希望帮到你。本文还有配套的精品资源点击获取
返回列表