MFC仿windows计算器
设计报告
一.题目:利用MFC框架编写简易计算器
要求使用MFC框架在Visual Studio 6.0环境下编写一个简易的计算器,支持任意位数的加减乘数,正负转换,并且实现BackSpace CE C功能。
二.设计过程
1. Windows消息处理机制的理解
首先编写程序需要对Windows程序的消息处理机制(Message Handle)有个比较清晰的了解。Windows的程序都是通过消息来传送数据,有不需要用户参与的系统消息,比如异常处理等。还有用户消息,比如鼠标的单击,双击,键盘的键入等。
2. 界面的设计
仿照Windows附件里面的计算器,在资源视图中画好界面,如图:
主要使用到Layout菜单中的Align功能对各个按钮进行对其,使界面更加整洁。拖出的控件有上面的一个Edit控件用于显示数字,Button控件用于处理鼠标的消息。
3. 建立的变量,控件的命名,对应的消息处理函数对应表
变量定义:
double poz; //保存小数点的位置,初始化为1,表示poz-1个小数点。
double m_Dis; //Edit控件上需要显示的数字
BOOL point_flag; //小数点表示位,判定是否是小数,是小数为1,不是小数为0。
double numfirst; //保存计算过程中的前一个数字,
double numsecond;//保存计算过程中的第二个数字
char op;//记录当前的计算符号,可以为’+’,’-’,’*’,’/’,’=’,’c’,’n’
变量初始化:
poz=1;
m_Dis = 0.0;
numfirst=0;
numsecond=0;
op=0;
4. 设计思路
a) 首先考虑对所有按键分为两类,数字类和符号类,0,1,2,3,4,5,6,7,8,9为数字类,+,-,*,/,=为符号类。数字在计算的过程中最多需要保存两个,所以定义了两个double型变量numfirst和numsecond来进行存储。符号需要一个char op来存储。
b) 然后考虑在计算的过程中,numfirst和numsecond的存储状态有三种,一种是numfirst==0 && numsecond==0 也就是程序刚开始运行还没有开始录入数字的状态。二种是numfirst!=0 && numsecond==0 也就是第一个数字已经录入,符号也已经录入时候把m_Dis的值直接赋值给numfirst,第三种是numfirst!=0 &&numsecond!=0,表示可以通过op来把两数合并为一个数。
c) 考虑到该计算器支持连续的计算,比如3.33+1.33*88/96= ?。所以必须在点符号Button也要计算出之前的结果,通过判断op,来计算,把两个数字合并为一个数字,方便下一次运算,功能近似于点=,所以把=也划分到符号类。
d) 因为数字全部使用的是double,键入的数字必须通过一定的处理达到累加的效果,加上小数和整数的处理差异性大,所以分别用point_flag来判断,分别出来小数和整数。
5. 成员函数及其释义
因为对OnNum0()到OnNum9()的处理函数差异仅在一个数字上,可以通过调用一个共同的函数OnCal(double num)来简化源代码长度,增加模块性。
void CCalcDlg::OnCal(double num)
{ //分三种状态来处理
if(numfirst!=0 && numsecond!=0)
{
if(point_flag==TRUE) //判定为小数
{
poz*=0.1; //小数进位
m_Dis=m_Dis+poz*num;//递增
UpdateData(false); //把结果从内存传递到屏幕
}
else
{
m_Dis=m_Dis*10+num;
UpdateData(false);
}
}
if(numfirst!=0 && numsecond==0)
{
if(point_flag==TRUE)
{
poz*=0.1;
m_Dis=m_Dis+poz*num;
UpdateData(false);
}
else//判定为整数
{
m_Dis=m_Dis*10+num;//递增
UpdateData(false);
}
}
if(numfirst==0 && numsecond==0)
{
if(point_flag==TRUE)
{
poz*=0.1;
m_Dis=m_Dis+poz*num;
UpdateData(false);
}
else
{
m_Dis=m_Dis*10+num;
UpdateData(false);
}
}
}
以OnAdd()为例子讲解符号的处理函数,函数的功能是先判定之前按下字符时op的值,更具op的值来进行相应的运算。
void CCalcDlg::OnAdd()
{ //根据numfirst和numsecond和op的值分为5种状态。
if(numfirst!=0 && numsecond==0&&op=='+')
{
numsecond=m_Dis;
numfirst=numfirst+numsecond; //之前按的是加把两个数赋值到前一个数
m_Dis=numfirst;//赋值给屏幕
numsecond=0; //从新赋值为0,清空,不影响下一次判断
UpdateData(FALSE);
m_Dis=0; //屏幕的值同时清空
}
if(numfirst!=0 && numsecond==0&&op=='-')
{
numsecond=m_Dis;
numfirst=numfirst-numsecond;
m_Dis=numfirst;
numsecond=0;
UpdateData(FALSE);
m_Dis=0;
}
if(numfirst!=0 && numsecond==0&&op=='*')
{
numsecond=m_Dis;
numfirst=numfirst*numsecond;
m_Dis=numfirst;
numsecond=0;
UpdateData(FALSE);
m_Dis=0;
}
if(numfirst!=0 && numsecond==0&&op=='/')
{
numsecond=m_Dis;
numfirst=numfirst/numsecond;
m_Dis=numfirst;
numsecond=0;
UpdateData(FALSE);
m_Dis=0;
}
if(numfirst==0 && numsecond==0)
{ //该状态为程序启动还没有开始录入输入的状态
numfirst=m_Dis; //屏幕的值赋值到numfirst
UpdateData(FALSE);
m_Dis=0;
}
op='+'; //最后记录最后一个操作是+
poz=1;//小数点位置归位
point_flag=FALSE;//默认小数点标志为整数,也就是0,也就是FALSE
}
“+/-“按钮的处理函数
void CCalcDlg::OnNeg()
{
m_Dis=-m_Dis;//换个符号,其他都一样
UpdateData(FALSE);
poz=1;
point_flag=FALSE;
}
‘.’按钮的处理函数
void CCalcDlg::OnPt()
{
point_flag=TRUE; //把标志位改为“小数点”状态
}
“Backspace”按钮的处理
void CCalcDlg::OnBackspace()
{
//主要通过_gcvt()和strtod()函数进行字符串和浮点数之间的转换
char buffer[30]; //定义个装字符的数组
_gcvt(m_Dis,sizeof(m_Dis),buffer); //把m_Dis存的数字转换为string
for(int i=0;i<30;i++)
{
if(buffer[i]=='.'&& buffer[i+1]==0)//判断是否为整数
{
point_flag=FALSE; //标志位设置为“整数位”
break;
}
}
if(point_flag==TRUE) //如果是小数
{
for(int j=0;j<30;j++)
{
if(buffer[j]==0)
{
buffer[j-1]=0; //把’\0’之前的字符赋值为’\0’,就相当于剪掉最后一位
break;
}
}
}
else //如果是整数
{
buffer[i-1]=0; //剪掉’.’之前那位
}
m_Dis=strtod(buffer,NULL);//再用strtod弄成浮点数
UpdateData(FALSE);
poz=1;
}
‘CE’按钮处理函数
void CCalcDlg::OnCe()
{
if(numfirst!=0 && numsecond==0)//CE只能修改第二个数字
{
m_Dis=0; //把屏幕的值赋值为0
UpdateData(FALSE);//并显示出来
}
}
‘C’按钮处理函数
void CCalcDlg::OnClear()
{
op=NULL; //清空符号
numfirst=0;//清空第一个数字
numsecond=0;//清空第二个数字
point_flag=FALSE;//改为默认整数位
poz=1;//小数点归位
m_Dis=0;
UpdateData(FALSE); //屏幕显示归0
}
键盘响应:
void CCalcDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch(nChar)
{
case VK_NUMPAD0:
OnNum0();break;
case VK_NUMPAD1:
OnNum1();break;
case VK_NUMPAD2:
OnNum2();break;
case VK_NUMPAD3:
OnNum3();break;
case VK_NUMPAD4:
OnNum4();break;
case VK_NUMPAD5:
OnNum5();break;
case VK_NUMPAD6:
OnNum6();break;
case VK_NUMPAD7:
OnNum7();break;
case VK_NUMPAD8:
OnNum8();break;
case VK_NUMPAD9:
OnNum9();break;
case VK_ADD:
OnAdd();break;
case VK_SUBTRACT:
OnMin();break;
case VK_MULTIPLY:
OnMul();break;
case VK_DIVIDE:
OnDiv();break;
case VK_BACK:
OnBackspace();break;
case VK_DECIMAL:
OnPt();break;
case VK_DELETE:
OnBackspace();break;
case VK_RETURN:
OnEqu();break;
}
CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}
BOOL CCalcDlg::PreTranslateMessage(MSG* pMsg)
{
SendMessage(pMsg->message,pMsg->wParam,pMsg->lParam);
return CDialog::PreTranslateMessage(pMsg);
}
总结:
本次MFC计算器的制作,学习到了MFC基本的编程方法,增加了小组开发的团结协作能力。对OOP编程的理解进一步加深。但是程序仍然没存在一定的问题,比如除数不能为0的Exception handle,符号键多次点击结果混乱。由于时间仓促,如果有更多的时间,必定这些问题会迎刃而解。通过这次课程设计,以后Windows 应用程序势必会轻车熟路。
第二篇:李晓奇 丁健华 吴继超等MFC计算器课程设计报告
MFC表达式计算器课程设计报告
班 级: 5班
学生姓名及学号:丁健华(02011507)
李晓奇(02011513)
吴继超(02011516)
完成日期:2012年10月13日
小组成员及分工:
李晓奇(主要负责Calculate.cpp代码的编写)
李晓奇(主要负责界面的设计和实现、辅助代码的编写)
丁健华(主要负责功能的改进与创新、辅助代码的编写)
丁健华(主要负责代码的链接、辅助代码的编写)
吴继超(主要负责报告的书写、辅助代码的编写)
题目:利用MFC框架编写简易表达式计算器
【分析】
一.设计过程
1.Windows消息处理机制的理解
首先编写程序需要对Windows程序的消息处理机制(Message Handle)有个比较清晰的了解。Windows的程序都是通过消息来传送数据,有不需要用户参与的系统消息,比如异常处理等。还有用户消息,比如鼠标的单击,双击,键盘的键入等。
2.界面的设计
1)界面的初步设计
仿照Windows附件里面的计算器,在资源视图中画好界面,如图:
2)修改每个static的属性
3)修改每个button的属性
结果如下图:
4)修改每个button的处理机制
在类向导Classwizard窗口中进行,如下图:
其他button按钮的修改类似
5)修改每个edit的类型和名称
在类向导Classwizard窗口中进行:
单击Add Variable按钮,在如下窗口中进行修改
其他edit的修改类似
最终结果如下:
注:主要使用到Layout菜单中的Align功能对各个按钮进行对齐,使界面更加整洁。拖出的控件有上面的一个Edit控件用于显示数字,Button控件用于处理鼠标的消息。
6)系统菜单的添加
在Menu的IDR_MENU1中添加系统菜单:
同理在“帮助”菜单中添加“关于”。
二. 设计步骤
1. 添加头文件
将Calculate.cpp(见附录)改为Calculate.h将其添加到计算器Dlg.cpp : implementation file中,如下:#include "Calculate.h"。
2.成员函数及其释义:
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMyDlg dialog
CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMyDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMyDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMyDlg)
DDX_Control(pDX, IDC_EDIT3, m_time);
DDX_Control(pDX, IDC_EDIT2, m_result);
DDX_Control(pDX, IDC_EDIT1, m_input);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
//{{AFX_MSG_MAP(CMyDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
ON_BN_CLICKED(IDC_BUTTON2, OnButton2)
ON_WM_TIMER()
ON_BN_CLICKED(IDC_BUTTON3, OnButton3)
ON_COMMAND(ID_ABOUT, OnAbout)
ON_COMMAND(ID_QUIT, OnQuit)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
////////////////////////////////////////////////////////////////////////////
// CMyDlg message handlers
3.OnButton1()按钮的处理函数
双击“等于(=)”按钮,添加如下代码:
void CMyDlg::OnButton1()
{
// TODO: Add your control notification handler code here
CString str;
char *ch;
m_input.GetWindowText(str);
ch = (LPSTR)(LPCTSTR)str;
char ch2[50];
strcpy(ch2,ch);
Cal a(ch2);
if(!a.OK)
{m_result.SetWindowText("表达式不合法!");// 表达式不合法,判别出来并给出相应的错误提示
}
else
{if(a.Sign)
{m_result.SetWindowText("除数为零!");// 表达式不合法,可以判别出来并给出相应的错误提示
}
else
{str.Format("%lf",a.GetV());
m_result.SetWindowText(str);
}
}
}
以OnButton1()作为求值处理函数,函数的功能是单击等于(=)按钮,运算结果显示在IDC_EDIT2中
4.OnButton2()按钮的处理函数
双击“清除(C)”按钮,添加如下代码:
void CMyDlg::OnButton2()
{
// TODO: Add your control notification handler code here
m_result.SetWindowText("0");
m_input.SetWindowText("");
m_input.SetFocus();
}
// 函数的功能是把上次输入的表达式清空
5.OnButton3()按钮的处理函数
双击“全清(A)”按钮,添加如下代码:
void CMyDlg::OnButton3()
{
// TODO: Add your control notification handler code here
m_input.SetWindowText("");
m_input.SetFocus();
}
//函数的功能是把上次输入的表达式和运算结果都清除
6.OnTimer(UINT nIDEvent) 处理函数
CWnd::SetTimer(1,500,NULL);//设置时间每隔500ms更新一次。
void CMyDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
CString ch[] = {"日","一","二","三","四","五","六"};
SYSTEMTIME st;
::GetLocalTime(&st);
CString str_temp;
str_temp.Format("%u/%u/%u 星期"+ch[st.wDayOfWeek]+" %u:%u:%u\n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
m_time.SetWindowText(str_temp);
CDialog::OnTimer(nIDEvent);
}//函数的功能是把运算结果显示在IDC_EDIT3中
7.OnAbout()处理函数
void CMyDlg::OnAbout()
{
// TODO: Add your command handler code here
CAboutDlg about;
about.DoModal();
}
//关于帮助和版权信息
8.OnQuit()处理函数
void CMyDlg::OnQuit()
{
// TODO: Add your command handler code here
OnOK();
}
//退出主程序菜单
9.全部代码如下:
// 计算器Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "计算器.h"
#include "计算器Dlg.h"
#include "Calculate.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMyDlg dialog
CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMyDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMyDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMyDlg)
DDX_Control(pDX, IDC_EDIT3, m_time);
DDX_Control(pDX, IDC_EDIT2, m_result);
DDX_Control(pDX, IDC_EDIT1, m_input);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
//{{AFX_MSG_MAP(CMyDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
ON_BN_CLICKED(IDC_BUTTON2, OnButton2)
ON_WM_TIMER()
ON_BN_CLICKED(IDC_BUTTON3, OnButton3)
ON_COMMAND(ID_ABOUT, OnAbout)
ON_COMMAND(ID_QUIT, OnQuit)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMyDlg message handlers
BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE);
// Set small icon
m_Mu.LoadMenu(IDR_MENU2);
SetMenu(&m_Mu);
CWnd::SetTimer(1,500,NULL);
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CMyDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMyDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
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;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMyDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CMyDlg::OnButton1()
{
// TODO: Add your control notification handler code here
CString str;
char *ch;
m_input.GetWindowText(str);
ch = (LPSTR)(LPCTSTR)str;
char ch2[50];
strcpy(ch2,ch);
Cal a(ch2);
if(!a.OK)
{
m_result.SetWindowText("表达式不合法!");
}
else
{
if(a.Sign)
{
m_result.SetWindowText("除数为零!");
}
else
{
str.Format("%lf",a.GetV());
m_result.SetWindowText(str);
}
}
}
void CMyDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
CString ch[] = {"日","一","二","三","四","五","六"};
SYSTEMTIME st;
::GetLocalTime(&st);
CString str_temp;
str_temp.Format("%u/%u/%u 星期"+ch[st.wDayOfWeek]+" %u:%u:%u\n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
m_time.SetWindowText(str_temp);
CDialog::OnTimer(nIDEvent);
}
void CMyDlg::OnButton2()
{
// TODO: Add your control notification handler code here
m_result.SetWindowText("0");
m_input.SetWindowText("");
m_input.SetFocus();
}
void CMyDlg::OnButton3()
{
// TODO: Add your control notification handler code here
m_input.SetWindowText("");
m_input.SetFocus();
}
void CMyDlg::OnAbout()
{
// TODO: Add your command handler code here
CAboutDlg about;
about.DoModal();
}
void CMyDlg::OnQuit()
{
// TODO: Add your command handler code here
OnOK();
}
三.总结:
在设计当中我认识到开发一项好的软件不是某一个人就能完成的任务。一个团队的小组,一个勤奋的小组非常重要,沟通是解决问题的就好办法。俗话也说得好:“不怕虎一样的敌人,就怕猪一样的队伍”。所以小组中的每个成员的能力与协作能力也是致关重要的。同时,不仅是团队内部的交流,更多需要协调好团队之间的交流。
其次呢,在设计过程中我了解了,在开发一个项目时一定要先做好规划,按照软件的开发过程,详细地写好每一个必要的文档。一般的规则是,写文档需要团队协作,这样就允许开发人员和文档编写者利用彼此的长处,取长补短。例如,如果预期读者是系统设计师,开发人员需要提供技术细节,然后文档编写者按照正确语法组织和编辑内容。软件文档的最主要目标是传达一个系统的技术要素和使用方法。第二个目标是提供软件开发过程中的需求,决策,行为,角色和责任的书面记录。只有实现了这两个目标,软件文档才真正提供了有意义的信息。软件的概念就是:程序+文档,程序就是文档,文档集成在程序中。它要求在选择开发环境时不仅要考虑环境对设计、开发的完美支持,而且要考虑对维护、文档的支持;它要求软件人员在设计、开发过程中要考虑维护问题、文档问题;它要求程序与文档存储在同一位置、同一系统中;它要求使用相同工具进行程序与文档的书写、检索;它要求在编写和维护程序的同时形成文档,在书写文档时编写、维护程序。程序与文档合一的概念不仅存在于系统的设计、开发阶段而且存在于系统的维护阶段,它贯穿软件的生命周期。
本次MFC简易表达式计算器的制作,学习到了MFC基本的编程方法,增加了小组开发的团结协作能力。对C++的OOP编程思想理解进一步加深。但是程序仍然存在一定的问题,比如除数不能为0的Exception handle,符号键多次点击结果混乱,输入的字符除了数字还有其他不合法的字符(如字母、空格和其它标点符号等)。由于时间仓促,如果有更多的时间,这些问题必定会迎刃而解。通过这次课程设计,可以很好的加深对C++的理解,以后对Windows 应用程序的开发势必会轻车熟路。
四、附录
1.Calculate.cpp源代码
#ifndef CALCULATE_H
#define CALCULATE_H
#include<String.h>
class Cal
{
public:
struct Tree
{
char ch[50];
Tree* Lchild;
Tree* Rchild;
};
public:
int Sign;
bool OK;
public:
Cal(char* Ex);
bool Judge(char* Ex);
void GetTree(char* Ex, Tree* Node);
double GetResult(Tree* Node);
int GetFig(char ch);
void Getstring(char* Ex, int s, char* ch);
double GetValue(char* ch);
double GetV();
void Drop(Tree *Node);
~Cal();
private:
Tree *Node;
double Result;
};
Cal::Cal(char* str)
{
Sign = 0;
OK = true;
char Ex[50];
strcpy(Ex,str);
if(Judge(Ex))
{
Node = new Tree;
GetTree(Ex,Node);
Result = GetResult(Node);
}
else
OK = false;
}
Cal::~Cal()
{
if(OK)
Drop(Node);
#ifdef MYDEBUG
cout<<"end"<<endl;
#endif
}
void Cal::GetTree(char* Ex, Tree* Node)
{
int length = strlen(Ex);
int tag = length;
char Bch[50];
int i;
#ifdef MYDEBUG
cout<<" Ex: "<<Ex<<endl;
#endif
if(Ex[length-1]==')')
{
int acout = -1;
for(i=length-2;i>=0;i--)
{
if(Ex[i]==')')
acout--;
if(Ex[i]=='(')
{
acout++;
if(acout==0)
{
tag = i;
break;
}
}
}
if(i < 0)
{
Sign = 1;
return ;
}
if(tag==0)
{
Ex[length-1]='\0';
Getstring(Ex,1,Bch);
GetTree(Bch,Node);
}
if(tag!=0)
{
int l_tag = tag;
int t = 0;
for(int j = tag - 1; j >= 0; j--)
{
if(Ex[j] == ')')
t--;
if(Ex[j] == '(')
t++;
if(Ex[j]=='+'||Ex[j]=='-'||Ex[j]=='*'||Ex[j]=='/'&&t==0)
{
l_tag = j;
if(Ex[j]=='+'||Ex[j]=='-')
break;
}
}
if(t==0)
{
Getstring(Ex,l_tag+1,Bch);
Tree *Rchild = new Tree;
Tree *Lchild = new Tree;
Node->Lchild = Lchild;
Node->Rchild = Rchild;
GetTree(Bch,Node->Rchild);
*(Node->ch) = Ex[l_tag];
*(Node->ch+1) = '\0';
Ex[l_tag] = '\0';
GetTree(Ex,Node->Lchild);
}
else
{
Sign = 1;
return ;
}
}
}
else
{
int t = 0;
for(i=length-1;i>=0;i--)
{
if(Ex[i]==')')
t++;
if(Ex[i]=='(')
t--;
if(Ex[i]=='+'||Ex[i]=='-'||Ex[i]=='*'||Ex[i]=='/'&&t==0)
if(tag==length)
{
tag=i;
if(Ex[tag]=='+'||Ex[tag]=='-'&&t==0)
{
Tree *Rchild = new Tree;
Tree *Lchild = new Tree;
Node->Lchild = Lchild;
Node->Rchild = Rchild;
Getstring(Ex,tag+1,Bch);
strcpy(Node->Rchild->ch,Bch);
Node->Rchild->Lchild=NULL;
Node->Rchild->Rchild=NULL;
*(Node->ch) = Ex[tag];
*(Node->ch+1) = '\0';
Ex[tag]='\0';
GetTree(Ex,Node->Lchild);
break;
}
}
if(tag!=length&&(Ex[i]=='+'||Ex[i]=='-')&&t==0)
{
tag = i;
Tree *Rchild = new Tree;
Tree *Lchild = new Tree;
Node->Lchild = Lchild;
Node->Rchild = Rchild;
*(Node->ch)=Ex[tag];
*(Node->ch+1) = '\0';
Getstring(Ex,tag+1,Bch);
GetTree(Bch,Node->Rchild);
Ex[tag]='\0';
GetTree(Ex,Node->Lchild);
break;
}
}
if(i<0 &&tag!=length)
{
Tree *Rchild = new Tree;
Tree *Lchild = new Tree;
Node->Lchild = Lchild;
Node->Rchild = Rchild;
Getstring(Ex,tag+1,Bch);
strcpy(Node->Rchild->ch,Bch);
Node->Rchild->Lchild=NULL;
Node->Rchild->Rchild=NULL;
*(Node->ch) = Ex[tag];
*(Node->ch+1) = '\0';
Ex[tag]='\0';
GetTree(Ex,Node->Lchild);
}
if(i<0 &&tag==length)
{
strcpy(Node->ch ,Ex);
Node->Lchild=NULL;
Node->Rchild=NULL;
}
}
}
double Cal::GetResult(Tree* Node)
{
if(Node->Lchild!=NULL&&Node->Rchild!=NULL)
{
switch(*(Node->ch))
{
case '+': return GetResult(Node->Lchild) + GetResult(Node->Rchild);
case '-': return GetResult(Node->Lchild) - GetResult(Node->Rchild);
case '*': return GetResult(Node->Lchild) * GetResult(Node->Rchild);
case '/':
if(GetResult(Node->Rchild) == 0)
{
Sign = 1;
return -1;
}
else
return GetResult(Node->Lchild) / GetResult(Node->Rchild);
}
}
else
{
double Value = GetValue(Node->ch);
// delete Node;
return Value;
}
}
bool Cal::Judge(char* Ex)
{
int len = strlen(Ex);
int t=0;
if(len == 1 )
if(Ex[0] == '+'||Ex[0] == '-'||Ex[0] == '*'||Ex[0] == '/')
return false;
for(int i =1; i < len; i++)
{
if((Ex[i-1] == '+'||Ex[i-1] == '-'||Ex[i-1] == '*'||Ex[i-1] == '/')
&&(Ex[i] == '+'||Ex[i] == '-'||Ex[i] == '*'||Ex[i] == '/'))
return false;
}
for(i = 0; i < len; i++)
{
if(Ex[i]==')')
t++;
if(Ex[i]=='(')
t--;
}
if(t!=0)
return false;
for(i=0;i<len;i++)
{
if(Ex[i]!='0'&&Ex[i]!='1'&&Ex[i]!='2'&&Ex[i]!='3'&&Ex[i]!='4'&&Ex[i]!='5'&&Ex[i]!='6'
&&Ex[i]!='7'&&Ex[i]!='8'&&Ex[i]!='9'&&Ex[i]!='('&&Ex[i]!=')'&&Ex[i]!='+'&&Ex[i]!='-'
&&Ex[i]!='*'&&Ex[i]!='/'&&Ex[i]!='\n'&&Ex[i]!='.')
return false;
}
return true;
}
void Cal::Getstring(char* Ex, int s, char* ch)
{
for(int i=s,j=0;i < strlen(Ex);i++,j++)
*(ch+j)=Ex[i];
ch[j]='\0';
}
double Cal::GetValue(char* ch)
{
int tag = 0;
double Value = 0;
double n = 1;
for(int i=0;i<strlen(ch);i++)
{
if(ch[i]=='.')
tag = 1;
else
{
if(tag == 0)
Value = Value*10+GetFig(ch[i]);
if(tag == 1)
{
n = n/10;
Value = Value + ((double)GetFig(ch[i])*n);
}
}
}
return Value;
}
int Cal::GetFig(char ch)
{
if(ch=='0')
return 0;
if(ch=='1')
return 1;
if(ch=='2')
return 2;
if(ch=='3')
return 3;
if(ch=='4')
return 4;
if(ch=='5')
return 5;
if(ch=='6')
return 6;
if(ch=='7')
return 7;
if(ch=='8')
return 8;
if(ch=='9')
return 9;
return 0;
}
double Cal::GetV()
{
return Result;
}
void Cal::Drop(Tree* Node)
{
if(Node->Lchild!=NULL)
{
Drop(Node->Lchild);
Drop(Node->Rchild);
delete Node;
}
else
delete Node;
}
#endif