C++简单计算器课程设计报告

简单计算器

1 基本功能描述

简单计算器包括双目运算和单目运算功能。双目运算符包含基本的四则运算及乘幂功能,单目运算符包含正余弦,阶乘,对数,开方,倒数等运算。可对输入任意操作数,包括小数和整数及正数和负数进行以上的所有运算并能连续运算。出现错误会给出相应提示,同时包含清除,退格,退出功能以及有与所有按钮相对应的菜单项。

2 设计思路

如图是整个计算器的流程图,打开计算器程序,输入数据,调用输入数据子程序。子程序开始时m_num为零。按下数字按钮后,判断m_num的正负号,对其进行相关处理,然后在判断是否有小数点,处理后输出结果。

回到总流程图中,输完第一个操作数后,若按下双目运算符,则把m_num存入num2,按下输入第二个操作数并算出结果。若按下的是单目运算符,则直接对其处理得出结果。若按下清零,则m_num等于0.在运算中还要判断运算是否合法,以及结果是否超出显示范围,以做出相关提示,按下清零。算出后把结果保存至num2。若需用算出的结果继续运算则回到输入完第一个操作数后,若不需要则判断试否需要开始新一轮的运算,不需要则结束。

https://upload.fanwen118.com/wk-img/img100/2734721_1.jpg

图1 输入数据子函数流程图

https://upload.fanwen118.com/wk-img/img100/2734721_2.jpg

图2 简单计算器总流程图

3 软件设计

3.1 设计步骤

打开Microsoft Visual C++ 6.0,在文件中点击新建,在弹出框内选择MFC AppWizard[exe]工程,输入工程名09107xuzhujie及其所在位置,点击确定,如图3所示。 https://upload.fanwen118.com/wk-img/img100/2734721_3.jpg

图3 新建MFC AppWizard[exe]工程

将弹出MFC AppWizard-step 1对话框,选择基本对话框,点击完成,如图4所示。

https://upload.fanwen118.com/wk-img/img100/2734721_4.jpg

图4 基本对话框

这样,MFC AppWizard就建立了一个基于对话窗口的程序框架,如图5所示。

https://upload.fanwen118.com/wk-img/img100/2734721_5.jpg

图5 新建的对话框

3.2 界面设计

1、创建控件

在图5所示的Resoure View选项卡中打开Dialog资源组,双击IDD_ 09107xuzhujie_DIALOG,在右边的窗口中显示出待编辑的对话框。开始摆放控件,包括编辑框和按钮的创建。按钮的创建以“1”为例进行介绍,其他按钮的创建可参照此进行操作。

1)在图5中Controls的“编辑框”按钮上单击鼠标左键,在对话框编辑窗口上合适的位置按下鼠标左键并拖动鼠标画出一个大小合适的编辑框。在编辑框上单击鼠标右键,在弹出的快捷莱单中选择属性选项,此时弹出Edit属性对话框,如图6所示,在该对话框中输入ID属性。

https://upload.fanwen118.com/wk-img/img100/2734721_6.jpg

图6 Edit属性对话框

2)在图3中Controls的“Button”按钮上单击鼠标左键,在对话框上的合适的位置上按下鼠标左键并拖动鼠标画出一个大小合适的下压式按钮。在按钮上单击鼠标右键,在弹出的快捷菜单中选择属性选项,此时也弹出Push Button属性对话框,如图7所示,在该对话框中输入控件的ID值和标题属性。

https://upload.fanwen118.com/wk-img/img100/2734721_7.jpg

图7 Push Button属性

按照上面的操作过程编辑其他按钮和编辑框等对象的属性。

表1 各按钮和编辑框等对象的属性

完成后界面如图8所示。

https://upload.fanwen118.com/wk-img/img100/2734721_8.jpg

图8 计算器界面

2、连接变量和代码

1) 给编辑框连接变量。在编辑框上单击鼠标右键,在弹出菜单中选择ClassWizard菜单项,弹出MFC ClassWizard对话框,选择Member Variables选项卡,如图9所示。

https://upload.fanwen118.com/wk-img/img100/2734721_9.jpg

图9 MFC ClassWizard对话框中的Menber Variable选项卡

在Control IDs列表框中选择IDC_xianshi,单击Add Variables按钮,在弹出的对话框中连接变量m_num。变量类型设定为doubel型。如图10所示。

https://upload.fanwen118.com/wk-img/img100/2734721_10.jpg

图10 m_num属性设置

2) 给“1”按钮连接代码。在“1”按钮上单击鼠标右键,选择ClassWizard菜单项,在弹出的对话框中选择Messages Maps选项卡,然后在Object IDs列表框中选择IDC_ADD_BUTTON,在Messages列表框中选择BN_CLICKED项,即选择单击按钮事件,如图11所示。

https://upload.fanwen118.com/wk-img/img100/2734721_11.jpg

图11 “1”按钮链接代码对话框

3、菜单的设计

选择 插入/资源命令,将显示插入资源对话框。如图12所示。选择Menu,点击新建即可。

https://upload.fanwen118.com/wk-img/img100/2734721_12.jpg

图12 插入资源对话框

Resoure View选项卡中打开Menu资源组,双击IDR_MENU1, 然后给菜单添加相应的标题,并把每个菜单对应的ID值与相应的按钮的一致。如图13所示。

https://upload.fanwen118.com/wk-img/img100/2734721_13.jpg

图13 菜单属性对话框

在对话框的任何一个空白域单击鼠标右键,然后在弹出的快捷菜单中选择Properties命令,弹出Dialog Properties对话框,如图14。在对话框中的菜单组合框中选择IDR_MENU1。

https://upload.fanwen118.com/wk-img/img100/2734721_14.jpg

图14 对话属性对话框

最终的得到的界面如图15。

https://upload.fanwen118.com/wk-img/img100/2734721_15.jpg

图15 计算器完整界面

3.3 关键功能的实现

1)操作数的输入

编辑框所关联的变量为m_num,程序开始时为零。每按下一个数字键按钮,就把它累加到m_num里并刷新显示。BOOL型变量ele来判断是否有小数点和一个整型数dint来表示小数点后的数据的位数。当ele=TRUE时,表示有小数点,否则则表示无小数点。按下小数点按钮后执行ele=TRUE。当按下一个数字键时,首先根据ele判断是否按下小数点,若有则把小数点后数的位数加1,dint则为此次键入的数所处的位数,将键入的数除以dint个10然后累加到编辑框所关联的变量m_num上,若没有则把m_num乘以10,然后累加到编辑框所关联的变量m_num上。

2)单目运算符功能的实现

由于单目运算符中必须有执行运算的函数,因而头文件中要加入math.h,如m_num=sqrt(m_num).按下按钮即可实现其功能。为实现连续运算,程序有所改动,参见(6)。

3)双目运算符功能的实现

双目运算符需要两个操作数才能运算,所以在执行双目运算符按钮的函数代码中,要把m_num中的值保存到变量num2中,然后把m_num清零。这样即可保存第一个操作数。另外先定义了一个整形变量addt,在不同的双目运算符中给addt赋不同的值,然后在等号中用switch语句来匹配addt值来判断需要执行何种双目运算。为实现连续运算,程序有所改动,参见(6)。

4)退格键功能的实现

当按下一次退格按钮时,若m_num为整数,只需对其中数据除以10再取整即可,若按下一次退格按钮为小数,则根据小数点后数据的位数dint进行操作,先对m_num执行(dint-1)次乘以10运算,然后取整,接着执行(dint-1)次除以10的运算即可。完成后需要把dint将减小1。

5) 正负号功能的实现

在头文件中定义一个BOOL型变量ele2来判断m_num的正负。ele2=TRUE时表示为正。在键入操作数时需要用到ele2。在正负号按钮的消息响应函数内容为m_num=-m_num;ele2=!ele2。

6)连续运算功能的实现

要连续运算必须将每次运算的结果保存下来。

对于双目运算符:首先在等号按钮的消息处理函数中在m_num清零之前将m_num的值赋给num2.不过这样的话如果再按双目运算符则num2的值将会被覆盖,于是在双目运算符按钮的消息处理函数中将“num2=m_num;”改为“if(m_num!=0)num2=m_num;”.这样若已经保存了结果则不进行覆盖。然后在每个数字按钮前加上“if(addt==0)num2=0;”,这样如果进行完一次运算之后按下任一数字键表示不需要连续运算,则将保存的结果删除,若按下的是双目运算符按钮则可以继续运算。

对于单目运算符:每次运算完之后都会对m_num清零,因此其结果也要赋给num2,下次运算是则会用到num2,于是第一次运算时直接将m_num 的值赋给num2,对其进行运算。以开根号为例,在消息处理函数代码中将“m_num=sqrt(m_num);”改为“num2=m_num;m_num=sqrt(num2); num2=m_num;”。但是第一次运算执行完成之后m_num被清零,所以之后将m_num 的值不能赋给num2,代码改为 “if(m_num>=0)m_num=sqrt(num2); num2=m_num;”。其他单目运算符同理。

为实现连续运算是不需要按下等号即可实现,又在所有的运算符按钮的消息处理函数代码前加上了“if(addt!=0)Onequal();”。既若之前有按下双目运算符和两个操作数则连续运算再按下任一运算符时会自动调用等号的处理函数将上次的运算完成并保存结果方便下一次的运算。

4 结论与心得体会

经过多番测试,得出结论为设计的计算器能够完成功能描述内容中的全部功能。

经过2个星期的VC++课程设计,我在老师和同学的共同帮助下,顺利完成该课程设计,收获很大。本学期我们开设了《面向对象程序设计与VC++实践》课MFC部分皆为需要实践内容,正所谓“纸上谈兵终觉浅,觉知此事要躬行。”所以在本学期课程设计是很及时、很必要的。这样不仅能加深我们对MFC程序设计的掌握,而且还及时、真正的做到了学以致用。主要是以下几点。

基本功要过硬。不能马虎经常犯犯小错误。提高自己找错纠错能力,通常错误总是那几种,多练习就熟练了。

要善于查找资料提高自己的资料查询能力,要善于利用网络来获得知识,获得帮助。开始着手程序设计时,有些功能感觉无从下手,都不知道要干什么。这些问题只要找到相关的资料就会得到提示从而理解所要解决问题的大致方向。有了明确的目的,就可以发挥自己的头脑来完成它了。

要善于思考。程序中有些实现难点和最初完成程序时免不了的许多不足就需要自己根据代码认真思考来解决。只有这样才能纠正程序的不足并使其更加人性化。

这次课程设计让我学到了很多,不仅是巩固了先前学的MFC程序设计知识,而且也培养了我的动手能力,更令我的创造性思维得到拓展。希望今后类似这样课程设计、类似这样的锻炼机会能更多些!

5 参考文献

[1] 揣锦华.面向对象程序设计与VC++实践.西安电子科技大学出版社,2005

[2] 张建宏,汤练兵,马德骏.计算机程序设计基础-C语言程序设计.科学出版社,2006

[3] 陈清华.Visual C++课程设计案例精选与编程指导.东南大学出版社,2004

6 思考题

1、程序中如何实现小数的显示与运算?

答:在09107xuzhujieDlg.h头文件中定义一个BOOL型变量ele来判断是否有小数点和一个整型数dint来表示小数点后的数据的位数。当ele=TRUE时,表示有小数点,否则则表示无小数点。按下小数点按钮后执行ele=TRUE。首先根据ele判断是否按下小数点,若有则把小数点后数的位数加1,dint则为此次键入的数所处的位数,将键入的数除以dint个10然后累加到编辑框所关联的变量m_num上。

2、描述等号对应的消息处理函数中需要处理的问题及相应的实现代码?

答:在双目运算符按钮函数代码中将整形变量addt赋值,然后在等号中用switch语句来匹配addt值来判断需要执行何种双目运算。并将结果保存以进行下次运算时使用 ,玩策划那个一些变量的清零。

实现代码如下:

switch (addt) //数据判断

{

case 0: //为实现连续按下两次等号后能清零

UpdateData(FALSE); //数据刷新

break;

case 1:

m_num=num2+m_num; //两数相加

UpdateData(FALSE); //数据刷新

break;

case 2:

m_num=num2-m_num; //两数相减

UpdateData(FALSE); //数据刷新

break;

case 3:

m_num=num2*m_num; //两数相乘

UpdateData(FALSE); //数据刷新

break;

case 4:

if (m_num==0) //在除法中,判断除数是否为0

GetDlgItem(IDC_xianshi)->SetWindowText("错误,0不能作除数");

else {m_num=num2/m_num;UpdateData(FALSE);}

break;

case 5:

m_num=pow(num2,m_num); //取模

UpdateData(FALSE); //数据刷新

break;

}

if(m_num>=1.7E308||m_num<-1.7E308)

GetDlgItem(IDC_xianshi)->SetWindowText("结果过大,无法显示");

num2=m_num;

m_num=0;

num1=0;

dint=0;

ele=FALSE;

addt=0;

ele2=TRUE;

7 附录

7.1 调试报告

1、编译时提示错误:

“C:\Documents and Settings\周同\桌面\09107xuzhujie\09107xuzhujieDlg.cpp(230) : error C2018: unknown character '0xa3'

C:\Documents and Settings\周同\桌面\09107xuzhujie\09107xuzhujieDlg.cpp(230) : error C2018: unknown character '0xbb'

C:\Documents and Settings\周同\桌面\09107xuzhujie\09107xuzhujieDlg.cpp(231) : error C2143: syntax error : missing ';' before 'if'”

原因为分号是在中文输入法状态下输入,将其改为英文输入状态下了分号即可。

2、重做时调用其他工程中的对话框资源后编译链接运行后还是本来有的那个对话框,后来将原先的对话框资源删除并将调用的对话框的ID改为原先的对话框的ID,再次编译链接运行之后没有反应了,经过多次摸索发现调用过来的对话框是关联了菜单了,而新建的工程里没有菜单,因此将调用的对话框取消与菜单的关联,再次再次编译链接运行之后一切正常。

3、用GetDlgItem(IDC_NAME_EDIT)->SetWindowText(" ");来提示操作错误时,总是没有任何效果,经老师帮助得以解决。

因为UpdateData(FALSE)是将变量传到前台显示,SetWindowText就是设置前台显示的新内容。所以在SetWindowText后如果还有UpdateData(FALSE)则其效果会被后者覆盖,因此程序中SetWindowText之后不用UpdateData(FALSE)即可。

4、当操作数超过10位数时,用backspace会出现错误结果,一番研究研究发现用int(m_num)取整有问题,int只有4个字节,当超过其范围时就会出错。因此将取整的方法改为先用fmod取其除以1的余数得到其小数位,然后用m_num减去其小数位即可。

7.2 测试结果

1.输入“90.5”,“-”,“60.7”,“+”,“2.36”,“ =”,并在菜单中同样输入这组操作,均得到结果如图16。

https://upload.fanwen118.com/wk-img/img100/2734721_16.jpg

图16 结果图1

结果正确,证明可以实现双目运算符的连续运算,菜单功能正常。

2.输入“901”,“+/-”,“backspace”,“+”,“120”,“sin”,结果如图17.

https://upload.fanwen118.com/wk-img/img100/2734721_17.jpg

图17 结果图2

因为π取的是3.1415926,因此与0.5有小小偏差,认为结果正确。证明可以实现单目运算符的连续运算与backspace回格功能。

3.输入“0”,“ln”,结果如图18.

https://upload.fanwen118.com/wk-img/img100/2734721_18.jpg

结果图18

证明错误提示功能实现正常。

7.3 关键源代码

1、09107xuzhujieDlg.h里变量的定义:

BOOL ele2; // ele定义为布尔值,判断当前值是否是正数还是负数

int addt;

int i;

int dint;//dent判断小数后的位数

BOOL ele; //ele2定义为布尔值,判断是否有小数点

double num2;

double num1;

2、程序运行时初始化和头文件:

m_num=0; //输出框清0

num1=0; //变量1为0

num2=0; //变量2为0

dint=0; //小数点位数

addt=0; //整数位数无

ele=FALSE; //当前无小数

ele2=TRUE;

09107xuzhujieDlg.cpp里加上头文件#include "math.h"

3、 数字键1的程序:(数字键2到9类似)

if(addt==0)num2=0;

if(ele2==TRUE)

m_num=m_num*10+1; //单击数字键1后把数据传送到输出框中

else m_num=m_num*10-1;

if (ele==TRUE) //当前是否有小数点,如果有

{

num1=1;

dint=dint++;

for (i=0;i<dint;i++)

{

num1=num1/10; //1/10,得到的小数放入输出框中

}

if (ele2==TRUE) //当前值是否是正数还是负数,如果是正数

m_num=(m_num-1)/10+num1; //前面不加负号

else

m_num=(m_num+1)/10-num1; //否则前面加负号

}

UpdateData(FALSE); //数据刷新

5、 加法键+的程序:(-,*,/,x,x^y的代码类似)

if(num2==0)

{num2=m_num; //把第二个值赋于输出框的成员变量

m_num=0; //输出框的成员变量清0

ele=FALSE; //当前小数无

ele2=TRUE; //当前符号为正

dint=0; //小数位数无

addt=1;

}

6、 Sqrt键的程序:

if(m_num!=0)num2=m_num;

if(m_num>=0)

{m_num=sqrt(num2); //对数进行开根号

UpdateData(FALSE); //数据刷新

num2=m_num;

}

else GetDlgItem(IDC_xianshi)->SetWindowText("错误,负数不能开根号");

m_num=0;

num1=0;

dint=0;

ele=FALSE;

ele2=TRUE;

7、n!键的程序:

if(m_num!=0)num2=m_num;

if(fmod(num2,1)==0)

{m_num=1;

for(i=1;i<=num2;i++)m_num*=i; //对数进行求阶乘

UpdateData(FALSE); //数据刷新

num2=m_num;

if(m_num>=1.7E308||m_num<-1.7E308)

GetDlgItem(IDC_xianshi)->SetWindowText("结果过大,无法显示");

}

else GetDlgItem(IDC_xianshi)->SetWindowText("不是整数无法进行阶乘运算");

m_num=0;

num1=0;

dint=0;

ele=FALSE;

ele2=TRUE;

8、1/x键的程序:

if(m_num!=0)num2=m_num;

if(num2!=0)

{

m_num=1.0/num2; //对数进行求倒数

UpdateData(FALSE);//数据刷新

num2=m_num;

if(m_num>=1.7E308||m_num<-1.7E308)

GetDlgItem(IDC_xianshi)->SetWindowText("结果过大,无法显示");

}

else GetDlgItem(IDC_xianshi)->SetWindowText("错误,0不能求倒数");

m_num=0;

num1=0;

dint=0;

ele=FALSE;

ele2=TRUE;

9、sin键的程序:(cos的代码类似)

if(m_num!=0)num2=m_num;

m_num=sin(num2*3.1415926/180); //对数进行正弦

UpdateData(FALSE); //数据刷新

num2=m_num;

m_num=0;

num1=0;

dint=0;

ele=FALSE;

ele2=TRUE;

10、ln键的程序:

if(m_num!=0)num2=m_num;

if(m_num>0)

{

m_num=log(num2); //对数进行求ln

UpdateData(FALSE); //数据刷新

num2=m_num;

}

else GetDlgItem(IDC_xianshi)->SetWindowText("错误,操作数需大于0");

m_num=0;

num1=0;

dint=0;

ele=FALSE;

ele2=TRUE;

11、=键的程序:

switch (addt) //数据判断

{

case 0: //为实现连续按下两次等号后能清零

UpdateData(FALSE); //数据刷新

break;

case 1:

m_num=num2+m_num; //两数相加

UpdateData(FALSE); //数据刷新

break;

case 2:

m_num=num2-m_num; //两数相减

UpdateData(FALSE); //数据刷新

break;

case 3:

m_num=num2*m_num; //两数相乘

UpdateData(FALSE); //数据刷新

break;

case 4:

if (m_num==0) //在除法中,判断除数是否为0

GetDlgItem(IDC_xianshi)->SetWindowText("错误,0不能作除数");

else {m_num=num2/m_num;UpdateData(FALSE);}

break;

case 5:

m_num=pow(num2,m_num); //取模

UpdateData(FALSE); //数据刷新

break;

}

if(m_num>=1.7E308||m_num<-1.7E308)

GetDlgItem(IDC_xianshi)->SetWindowText("结果过大,无法显示");

num2=m_num;

m_num=0;

num1=0;

dint=0;

ele=FALSE;

addt=0;

ele2=TRUE;

12、清除键Clear的程序:

m_num=0; //输出框清0

num1=0; //变量1为0

num2=0; //变量2为0

dint=0; //小数无位数

addt=0; //整数位数无

ele=FALSE; //当前无小数

ele2=TRUE;

UpdateData(FALSE); //数据刷新

13、退格键Backspace的程序:

if (ele==FALSE ) //判断当前是否有小数点,如果没有

m_num=m_num/10-fmod(m_num/10,1); //去掉小数位

else

{

if (ele==TRUE && dint>0) //如果是小数

{

for (i=0;i<dint-1;i++) //移动小数点

{

m_num=m_num*10; //移动至只有一个小数位

}

m_num=m_num-fmod(m_num,1); //去掉小数位

for (i=0;i<dint-1;i++) //小数点移至原来位置

{

m_num=m_num/10;

}

}

dint=dint--;

}

if (dint==0)ele=FALSE;

if(m_num==0)ele2=TRUE;

UpdateData(FALSE); //数据刷新

14、退出键Exit的程序:

OnOK() ;

15、关联关于对话框代码:

CAboutDlg dlgAbout;

dlgAbout.DoModal();

相关推荐