课程设计实验报告
题 目
学 院
专 业
班 级
课程名称
学 号
学生姓名
指导教师
成 绩
二○##年一月
目 录
1 概述
1.1 课程设计目的
1.2 课程设计内容
1.3 课程设计思想
2 系统需求分析
2.1 系统目标
2.2 主体功能
2.3 开发环境
3 系统概要分析
3.1 设计步骤
3.2 界面设计
3.3 主要代码
4 结论与心得体会
4.1 遇到的问题与解决方法
4.2 心得
5 附录
5.1 测试
5.2 程序源代码
1 概述
1.1 课程设计目的
《面向对象程序设计》是一门实践性很强的计算机专业基础课程,课程设计是学习完该课程后进行的一次较全面的综合练习。其目的在于通过实践加深学生对面向对象程序设计的理论、方法和基础知识的理解,掌握使用C++语言进行面向对象设计的基本方法,提高运用面向对象方法分析实际问题、解决实际问题的能力。
1、巩固并加深学生对C++语言程序设计知识的理解;
2、培养学生面向对象的程序设计思想,使学生认识面向过程和面向对象两种设计方法的区别;
3、进一步掌握和应用VS 2010集成开发环境;
4、提高运用C++语言解决实际问题的能力;
5、掌握书写程序设计开发文档的能力(书写课程设计实验报告)。
1.2 课程设计内容
课题名称:功能齐全的计算器
说明:利用MFC实现一个功能齐全的计算器
要求:
1、实现的基本功能有:加、减、乘、除、计算带括号的运算式;
2、扩展功能有开平方、求三角函数、阶乘、自然底数e的幂、10的幂和普通数的幂、进制的转化等;
3、输入的原始数据、运算中间数据和结果都显示在窗口顶部的同一个编辑框中;
4、计算功能基本上是用系统内部函数;
5、“归零”按钮可以清除所有已输入的数据从头计算;
6、“←”按钮可以退位。
1.3 课程设计思想
如流程图1所示,先在编辑框中输入运算式,通过IsChar()函数判断表达式中是否有复杂的运算符。若无,则进行简单的双目运算;若有,则通过String_Replace()函数将IsChar()函数无法识别的运算符或字符用一定方式或浮点数替换,再进行简单的双目运算。
2 系统需求分析
2.1 系统目标
我们在学习生活中,常会遇到一些繁杂的数值运算,这时候我们就必须用到科学计算器,所以便着手开发了这个计算器程序,以便用于自己的学习工作。要计算功能有以下几个方面:加法,减法,乘法,除法,求幂,求平方根,求Sin,求Cos。
2.2 主体功能
1、十进制数的加、减、乘、除、乘方、阶乘等简单计算。
2、科学计算函数,包括正弦、余弦、正切、开方、指数等函数运算。
3、实现括号的优先级判断。
4、将编辑框里的内容以转化成二、八、十六进制。
2.3 开发环境
Windows 操作系统,Microsoft Visual Studio 2010。
3 系统详细设计
3.1 设计步骤
打开VS2010,在文件中点击新建,在弹出框内选择MFC AppWizard[exe]工程,输入工程名Calculator及其所在位置,点击确定,如图2所示。
图2新建MFC AppWizard[exe]工程
选择基于对话框,如图3所示
图3 基于对话框
3.2 界面设计
设计如图4所示的计算机界面,右击创建类向导,如表1所示,为每个控件添加ID号和单击响应事件的消息处理(BN_CLICKED)。
图4 计算机主界面
表1 各按钮和编辑框等对象的属性
如图5所示,为IDC_EDIT1和IDC_RADIO1添加变量。
图5 添加变量
3.3 主要代码
1)“=”键单击响应代码
void CCalculatorVersion3Dlg::OnBtnequal()
{
UpdateData();
m_str = m_string;
while ( ! IsChar(m_str))// 判断表达式中是否有其他字符
{
Calculate(m_str);
}
double sum = Simple( m_str );
if( sum > -9.9002032198673601e-10 && sum < 9.9002032198673601e-10) { sum = 0;}
if( sum > 0.4999999 && sum < 0.5000001) { sum = 0.5;}
if( sum < -0.4999999 && sum > -0.5000001) { sum = -0.5;}
char answer[30] ;
gcvt(sum, 20, answer); //把浮点数转换成字符串,同时返回一个指向字符串的存储位置的指针
SetDlgItemText(IDC_EDIT1,answer);
}
2)简单双目运算
double CCalculatorVersion3Dlg::Simple(string str)
{
double a = 0 ;
double b,c;
char op;
istringstream sin( str ); //定义字符流
for ( sin >>b ; sin>>op>>c; ) //分别读入运算符和运算符前后¨两个浮点数
{
switch( op )
{
case '-': // 减法运算
c = - c;
case '+': // 加法运算
a += b;
b = c;
break;
case '*': // 乘法运算
b *= c;
break;
case '/': // 除法运算
if ( c == 0 )
{
MessageBox("Can't be divided by zero");break;
}
b = b / c ;
break;
case '%': // double 数据的取模
if ( (long)c != 0 )
b = fmod( b , c );
break;
default:
break;
}
}
return a + b;
}
3)判断是否有简单双目运算以外的字符或运算符
BOOL CCalculatorVersion3Dlg::IsChar(string str)
{
int i;
for ( i = 0 ; i < str.length (); i ++ )
{
if ( ! (( str[i] >= '0' && str[i] <= '9' ) || (str[i] == '+') || (str[i] == '-') ||
(str[i] == '*') || (str[i] == '/') || (str[i] == '.') || ( str[i] == 'e') || ( str[i] == '%') ))
return FALSE;
}
return TRUE;
}
4)转换函数
void CCalculatorVersion3Dlg::String_replace(string &strBig, const string &strsrc, const string &strdst)
{
{
string::size_type pos=0;
string::size_type srclen=strsrc.size();
string::size_type dstlen=strdst.size();
while( (pos=strBig.find(strsrc, pos)) != string::npos)
{
strBig.replace(pos, srclen, strdst);
pos += dstlen;
}
}
}
5)复杂运算
double CCalculatorVersion3Dlg::Calculate(string &str)
{
// 递归计算括号内的值
if ( IsChar(str) )
{
double tok = Simple(str);
char token[20] ;
gcvt(tok,20,token);
String_replace( m_str, str, token );
}
else
{
// 是否有PI
while ( str.find ("PI") != -1 )
{
String_replace(m_str, str.substr(str.find ("PI"), 2 ), PI);
}
// 是否有e
while ( str.find ("EI") != -1 )
{
String_replace(m_str, str.substr(str.find ("EI"), 2 ), EI);
}
// 是否有括号
while ( str.find(")") != -1 )
{
int rightBrk = str.find(")");
int leftBrk = str.rfind ("(",rightBrk);
str.erase( rightBrk, 1 );
str.erase( leftBrk, 1 );
return Calculate(str.substr (leftBrk, rightBrk - leftBrk - 1) );
}
// 是否有sqrt
while ( str.find ("sqrt") != -1 )
{
int m1 = str.find ("sqrt");
int r1 = FindRight(m1 + 3);
double c1 = sqrt(atof(str.substr (m1 + 4 ,r1-m1-3).c_str()));
char ch1[20];
gcvt(c1,20,ch1);
String_replace(m_str,str.substr(m1,r1-m1+1), ch1);
}
// 是否有log
while ( str.find ("log") != -1 )
{
int m2 = str.find ("log");
int r2 = FindRight(m2 + 2);
double c2 = log10(atof(str.substr(m2 + 3,r2-m2-2).c_str()));
char ch2[20];
gcvt(c2,20,ch2);
String_replace(m_str,str.substr(m2,r2-m2+1),ch2);
}
// 是否有ln
while ( str.find ("ln") != -1 )
{
int m3 = str.find ("ln");
int r3 = FindRight(m3 + 1);
double c3 = log(atof(str.substr(m3 + 2,r3-m3-1).c_str()));
char ch3[20];
gcvt(c3,20,ch3);
String_replace(m_str,str.substr(m3,r3-m3+1),ch3);
}
// 是否有阶乘
while ( str.find ("!") != -1 )
{
int m4 = str.find("!");
int l1 = FindLeft(m4);
int c4 = atoi(str.substr(l1,m4-l1).c_str());
int sum = 1;
for ( int m = 1 ; m <= c4; m++ )
sum *= m;
char ch4[20];
itoa(sum,ch4,10);
String_replace(m_str,str.substr(l1,m4-l1+1),ch4);
}
// 是否有三角函数
while ( str.find ("sin") != -1 )
{
int m5 = str.find ("sin");
int r5 = FindRight(m5 + 2);
double c5 = sin(atof(str.substr(m5 + 3,r5-m5-2).c_str())/180*pi);
if( c5 > -9.9002032198673601e-10 && c5 < 9.9002032198673601e-10) { c5 = 0;}
if( c5 > 0.4999999 && c5 < 0.5000001) { c5 = 0.5;}
if( c5 < -0.4999999 && c5 > -0.5000001) { c5 = -0.5;}
char ch5[20];
gcvt(c5,20,ch5);
String_replace(m_str,str.substr(m5,r5-m5+1),ch5);
}
while ( str.find ("cos") != -1 )
{
int m6 = str.find ("cos");
int r6 = FindRight(m6 + 2);
double c6 = cos(atof(str.substr(m6 + 3,r6-m6-2).c_str())/180*pi);
if( c6 > -9.9002032198673601e-10 && c6 < 9.9002032198673601e-10) { c6 = 0;}
if( c6 > 0.4999999 && c6 < 0.5000001) { c6 = 0.5;}
if( c6 < -0.4999999 && c6 > -0.5000001) { c6 = -0.5;}
char ch6[20];
gcvt(c6,20,ch6);
String_replace(m_str,str.substr(m6,r6-m6+1),ch6);
}
while ( str.find ("tan") != -1 )
{
int m7 = str.find ("tan");
int r7 = FindRight(m7 + 2);
double c7 = tan(atof(str.substr(m7 + 3,r7-m7-2).c_str())/180*pi);
if( c7 > -9.9002032198673601e-10 && c7 < 9.9002032198673601e-10) { c7 = 0;}
if( c7 > 0.4999999 && c7 < 0.5000001) { c7 = 0.5;}
if( c7 < -0.4999999 && c7 > -0.5000001) { c7 = -0.5;}
char ch7[20];
gcvt(c7,20,ch7);
String_replace(m_str,str.substr(m7,r7-m7+1),ch7);
}
// 是否有“^”
while ( str.find ("^") != -1 )
{
int md = str.find("^");
int left = FindLeft(md);
int right = FindRight(md);
char c[20];
double aa, bb;
aa = atof(str.substr(left,md - left ).c_str());
bb = atof(str.substr (md + 1, right - md ).c_str());
gcvt( pow( aa ,bb ),20,c);
String_replace(m_str, str.substr(left,right - left + 1 ), c);
}
}
return 0;
}
6)小数点的左部和右部
int CCalculatorVersion3Dlg::FindLeft(int m)
{
for ( int i = m - 1; i >= 0 ; i -- )
{
if ( ! (( m_str[i] >= '0' && m_str[i] <= '9' ) || ( m_str[i] == '.')))
return i + 1;
}
return 0;
}
int CCalculatorVersion3Dlg::FindRight(int m)
{
for ( int j = m + 1; j < m_str.length() ; j ++ )
{
if ( ! (( m_str[j] >= '0' && m_str[j] <= '9' ) || ( m_str[j] == '.')))
return j - 1;
}
return m_str.length() - 1;
}
4 结论与心得体会
4.1 遇到的问题与解决方法
1、Debug Assertion Failed.
File: dlgdata.cpp
Line: 43
Solution: 编辑框Edit1属性选Group
2、Debug Assertion Failed.
File: dlgdata.cpp
Line: 269
Solution: 单选IDC_RADIO1的属性选Group,其他不设
3、ASSERTION::IsWindow(m_hWnd) 断言
Solution: 消除野指针
4.2 心得
这学期上过WINDOWS高级编程,书上有一个简易的计算器的制作,但是对于利用MFC这个类库制作能进行科学运算的方法还不是很了解。
这个计算器我命名为VERSION 3.0的原因,是我在这个版本之前还做了两个计算器。第一个不能实现科学的单目运算,第二个能进行三角函数等运算但不能实现括号。后来将网上下载来的资源整理后发现substr()、find()等函数的功能,才写出这个计算器。
感觉写得并不是很好,因为还有一些问题没有得到完全的解决,比如sin函数与括号的结合问题,至今还没找到解决的方法,只能以后有空再好好想想了!
本来还想上网学一下MFC的美化,把这个页面做得好看一些的,但时间不允许了,所以只是在同学的指点下添加了计算器的背景图。
总的来说,这次的课程设计,还是让自己学到不少东西,同时也感觉到自己现在的不足——基础不够扎实,很多东西都是一知半解,真的要好好加强对基础知识的学习了!很多问题,不仅仅只是把功能实现就行了,还要注意代码的优化,把结果做得更好!
5 附录
5.1 测试
1)(7-9)/5
结果正确,证明可以实现双目运算符的连续运算,菜单功能正常
2)sin30
运算结果证明单目运算符能运行成功
3)8/0
证明错误提示功能实现正常
5.2 程序源代码
// CalculatorVersion3Dlg.cpp : 实现文件
#include "stdafx.h"
#include "CalculatorVersion3.h"
#include "CalculatorVersion3Dlg.h"
#include "afxdialogex.h"
#include <string>
#include <sstream>
#include <cmath>
using namespace std;
#define pi 3.141592653579893
const char * PI = "3.141592653579893";
const char * EI = "2.718281828459045";
……
void CCalculatorVersion3Dlg::OnBtnclear()
{
m_string = "";
UpdateData(FALSE);
}
void CCalculatorVersion3Dlg::OnRadio1()
{
m_num = 0;
UpdateData();
int answer1 = (int)atof(m_string.operator LPCTSTR());
itoa(answer1,(char *)m_string.operator LPCTSTR(),2);
UpdateData(FALSE);
}
void CCalculatorVersion3Dlg::OnRadio2()
{
m_num = 1;
UpdateData();
int answer = (int)atof(m_string.operator LPCTSTR());
itoa(answer,(char *)m_string.operator LPCTSTR(),8);
UpdateData(FALSE);
}
void CCalculatorVersion3Dlg::OnRadio3()
{
m_num = 2;
UpdateData();
int answer = (int)atof(m_string.operator LPCTSTR());
itoa(answer,(char *)m_string.operator LPCTSTR(),10);
UpdateData(FALSE);
}
void CCalculatorVersion3Dlg::OnRadio4()
{
m_num = 3;
UpdateData();
int answer = (int)atof(m_string.operator LPCTSTR());
itoa(answer,(char *)m_string.operator LPCTSTR(),16);
UpdateData(FALSE);
}// 进制的转换
void CCalculatorVersion3Dlg::OnButton0()
{
m_string += "0";
UpdateData( FALSE );
}// 1~9和其他按钮类似
void CCalculatorVersion3Dlg::OnBtnback()
{
m_string.Delete( m_string.GetLength() - 1, 1 );
UpdateData( FALSE );
}// 撤销键
void CCalculatorVersion3Dlg::OnBtnei()
{
m_string += "EI^";
UpdateData( FALSE );
}
void CCalculatorVersion3Dlg::OnBtnpi()
{
m_string += "PI";
UpdateData( FALSE );
}
void CCalculatorVersion3Dlg::OnBtnsin()
{
m_string += "sin";
UpdateData( FALSE );
}
……