c++课程设计分数计算器.docx
《c++课程设计分数计算器.docx》由会员分享,可在线阅读,更多相关《c++课程设计分数计算器.docx(19页珍藏版)》请在冰豆网上搜索。
c++课程设计分数计算器
C++课程设计报告
题目:
分数计算器
年级:
专业:
通信工程
姓名:
指导老师:
难度B级
教师评语:
1.程序功能介绍
分数计算器及测试程序,具有对输入的运算进行“+,-,*,/,^”的功能
2.程序设计要求
(1)完成计算器程序,对main函数的结构进行改进,要求有详细的菜单选项界面,用户不选择退出就可以反复运算。
(2)完善程序,改为多操作数基本四则运算,遇#停止。
(3)增加函数,完成四则混合运算,注意算法要考虑运算符的优先级,对应增加主菜单选项。
(4)增加语句使四则运算具有测试功能,即计算机能够自动出题,并要求用户计算,同时计算机判断用户计算的对错并打分,要求十题一个单元,每题一个运算符,参与运算的分子分母均为一位数,运算符包括加,减,乘,除及大于小于等逻辑运算符。
3.基本数据结构
含有整型变量,字符变量,实型变量,还定义了一个分数类;
除此之外还有以下函数:
1。
分数转换为小数;2。
分数转换为带分数;3。
实现简单计算的函数;4。
混合运算函数;5。
随机测试题函数;6。
清屏函数。
此外还多处运用了运算符的重载
4.程序运行结果界面
1.初始界面
2.功能1
3.功能2
4.功能3
5.功能4
6.功能5
5.课程设计遇到的技术问题与解决思路
问题1:
分数的表示
解决办法:
建立函数,分别置分母,分子,同时构造约分函数
问题2:
真分数的表示
解决办法:
构造函数,置整数部分和分数部分
书本提供的源代码虽然较为完备,但也存在一些错误。
问题3:
乘方的函数功能实现,书本提供的原有的类中重载的“^”运算符无法实现乘方运算
解决办法:
考虑到“^”运算符作为位运算符,不能按照双目运算符的重载格式进行重载,故,将用于代码内部的“^”运算符改为“%”双目运算符进行重载,进行重载运算,但对外输入输出,仍使用作为字符常量的^,解决了源代码中,分数运算乘方无法实现的问题。
fractionoperator%(constfraction&base,intexp)//^运算符并非双目运算符
//不可按照双目运算的方法重载,故修改为%,进行内部操作
{
fractionreturnValue;
returnValue.setnum(pow(base.numerator(),exp));
returnValue.setden(pow(base.denominator(),exp));
returnValue.simplify();
returnreturnValue;
}
问题4:
输出运算符<<重载不完善,因为对于函数参数分数首先进行了取绝对值运算,从而导致对于输出负分数的时候,无法正确输出。
解决办法:
重新对重载函数进行改写,先判断原参数分数的正负,若为负,则先输出“-”后,再取绝对值,进行分子分母的分别输出操作。
而且原代码中对于分数正负的判断极为繁琐,故直接调用已经重载过的“>”进行判断。
ostream&operator<<(ostream&os,constfraction&f)
{
if(f.decimal()<0)//Thisextremelycomplexandseemingly
os<<"-";//uselessifstatementteststoseeif
//fractionfisnegative.现已修改
fractiontemp=f.absval();
intnumer=f.numerator();
intdenom=f.denominator();
if(temp.numerator()==0||temp.denominator()==1)
os<else
os<returnos;
}
问题5:
考虑到人性化的操作,实现用户不选择退出,就可以实现持续的操作,故设计了一系列的多级菜单,从而实现用户结束一个操作之后,可以选择继续本操作。
如果不继续操作,则可以选择回到上一个菜单,或者回到主菜单,或者直接退出。
但是为了实现这一系列的选择操作,可是费了一番功夫。
解决办法:
(1)首先对于是否继续的选择操作,定义的字符型变量ch,为了防止变量名重复,主菜单对应的每一个操作,在ch后都设置了不同的数字,与主菜单后的每个操作前数字一致。
利用if语句进行判断。
如果,用户选择不继续本次操作,那么就输出另一个选择菜单,选择是返回上一级的菜单还是直接退出程序,并且具备了一定的异常处理功能,如果输入错误的指令,则会显示出来,要求用户重新输入代码,为了大大简化程序,此功能利用goto语句实现。
loop1:
if(ch1=='N'||ch1=='n')
{
cout<<"1.返回主菜单;2,直接退出程序\n";
charch11;
cin>>ch11;
switch(ch11)
{
case'1':
main();
case'2':
exit(0);
default:
{cout<<"输入指令不正确,请重新输入!
\n";gotoloop1;}
}
}
(2)关于实现在函数执行过程中返回主函数,可谓大费周章了,首先通过查资料得知,在main()内部返回主函数开头,可以在语句中直接使用main();语句。
但是,在外部定义的函数中,不能使用该语句,否则将出现errorC2065:
'main':
undeclaredidentifier
errorC2373:
'main':
redefinition;differenttypemodifiers
这两个语法错误,为了解决该问题,想到了使用goto语句,但是该语句无法在函数间跳跃,故而只得作罢。
然后又想到利用返回值进行判断,但是,每次判断是,则要执行被调函数,再一次运行一次混合运算函数,或者是测试函数,显然是不可以的。
最终,查了一些资料,获得启发。
无返回值类型的函数,通过return;语句可以直接跳出该函数。
于是定义了一个全局变量judgement,当遭遇需要返回主菜单时,便将judgement进行赋值为1,随后跳出,对judgement进行判断,从而判断是否返回主菜单,如是,则配合主函数中的main()返回到主函数开头。
(3)在随后要讲到的异常处理部分,当检测到被除数为0的时候,则弹出提示消息,并且可以选择是否重新输入该数字,或者直接返回上一级菜单。
if(f2==0)
{
cout<<"被除数不能为0!
!
\n1.重新输入该数。
2.返回主菜单。
\n";
intk4;
cin>>k4;
if(k4==1)
{
cin>>f2;
gotoloopsmall1;
}
else{judgement=1;return;}//由此实现由外部函数直接回到主函数头部
}
if(judgement==1)main();
问题6:
混合运算的核心问题即是优先级的控制;
解决办法:
为了实现运算的优先级,则需要综合判断前后的两个运算符,首先算出优先级较高的运算符两边的数字,再对剩余的那个数字和结果进行运算,算出新的结果。
在输入的同时进行运算。
为了将运算不断向前推进,则定义了3个分数类的对象result,f1,f2.以及两个代表前后运算符的字符型变量op1,op2。
并通过对其不断赋值,如op1=op2,实现运算的推进.具体操作通过switch的嵌套实现。
过程及其繁琐,而且一旦语句出现偏差,极易出现难以发现的运行错误,即最终计算结果不正确。
一下为函数的定义:
//==========================================混合运算====================================================================
voidcomplex()
{
fractionf1,f2,result;
charop1,op2;
cout<<"ATTENTION:
为保证计算的准确性,若需要输入整数X进行计算,请将该整数以X/1的形式进行输入!
!
!
";
cout<<"请依次输入分数(整数)和运算符,并以#结束~\n";
op1='';
cin>>f1>>op2;
while(op2!
='#')
{
judgement=0;
cin>>f2;
switch(op1)
{
case'':
result=f1;f1=f2;op1=op2;break;
case'+':
{
switch(op2)
{
case'+':
case'-':
result=result+f1;f1=f2;op1=op2;break;//
case'*':
f1=f1*f2;break;
loopsmall1:
case'/':
{
if(f2==0)
{
cout<<"被除数不能为0!
!
\n1.重新输入该数。
2.返回主菜单。
\n";
intk4;
cin>>k4;
if(k4==1)
{
cin>>f2;
gotoloopsmall1;
}
else{judgement=1;return;}//由此实现由外部函数直接回到主函数头部1111
}
f1=f1/f2;break;//op1=op2
}
}
}break;
case'-':
{
switch(op2)
{
case'+':
case'-':
result=result-f1;f1=f2;op1=op2;break;//
case'*':
f1=f1*f2;break;
loopsmall2:
case'/':
{
if(f2==0)
{
cout<<"被除数不能为0!
!
\n1.重新输入该数。
2.返回主菜单。
";
intk4;
cin>>k4;
if(k4==1)
{
cin>>f2;
gotoloopsmall2;
}
else{judgement=1;return;}//1111
}
f1=f1/f2;break;
}
}
}break;
case'*':
result=result*f1;f1=f2;op1=op2;break;//
case'/':
{
if(f1==0)
{cout<<"倒数第二个数为0,不能作为被除数!
!
!
现在返回主菜单\n";
judgement=1;return;//11111
}
result=result/f1;
f1=f2;op1=op2;break;//
}
}//switch(op1)end
cin>>op2;
}
switch(op1)
{
case'+':
cout<<"结果为:
"<case'-':
cout<<"结果为:
"<case'*':
cout<<"结果为:
"<loopsmall3:
case'/':
if(f1==0)
{
cout<<"被除数不能为0!
!
\n1.重新输入该数。
2.回到主菜单。
";
intk4;
cin>>k4;
if(k4==1)
{
cin>>f1;
gotoloopsmall3;
}
else{judgement=1;return;}//1111
}cout<<"结果为:
"<}
}
//======================================混合运算===========================================================
问题7:
异常处理,因为涉及四则运算,所以当出现被除数为0的情况,必须进行反应。
解决方案:
(1)首先是分数分母赋值为0的情况,这个在分数类的成员函数定义时就设置了反应,结果是直接退出程序。
voidfraction:
:
setden(intnewden)
{
if(newden!
=0)
d=newden;
else
{
cout<<"错误:
试图将分母设为零,不可以啊!
!
将直接退出程序!
!
\n"
<<"按任意键退出程序\n";
exit(0);
}
}
(2)然后是在运算的计算过程中,当出现被除数为0的情况,会出现提示,并且选择是否重新输入,或者返回上级菜单,重新输入通过goto语句跳转到输入语句实现。
返回上级菜单一是通过goto语句实现,若要返回到主菜单,则需要用到上面返回主函数开头的程序代码。
,其中特别注意,在混合运算函数中,需要特别设置标识loop的位置,否则经过运算符赋值,会导致计算结果错误。
(3)在随机测试的时候,取随机数的时候可能会在分母上取到0,为此,必须现在代入计算时进行检验,以防在测试过程中出现异常,而导致测试中断。
t1=rand()%10;
t2=rand()%10;
while(t1==0||t2==0)
{
t1=rand()%10;
t2=rand()%10;
}
问题8:
四则运算,菜单选择实现
都通过switch多分支语句实现,特别要注意大括号问题,嵌套一旦复杂,极易出现漏括号的现象。
因此需要注意括号的对齐,格式问题。
心得体会:
1.一个较复杂的,实现各种功能的程序,在设计的时候,可以将其不同的功能先单独书写,先行编译连接运行,调试过程中进行修改,这样比较清楚。
如果放在整个大程序中,时常会不知道问题出在哪里。
2.有时候,编译错误并不可怕,当编译没有错,但是运行结果不对时,找出BUG比较困难,必须重新从算法逻辑的角度考虑问题。
3.编写一个较大型的程序过程中,需要考虑的问题非常多,比如遇到异常输入,输入不符合规则该如何处理等等,细节问题太多了,这时,一个良好的编程习惯非常重要。
就像有时候会因为少括号而出现编译错误,括号加错地方而产生运行错误,但要是能养成括号书写对齐的习惯的话,很多这样的问题是能够避免的。