第七章c++程序设计.docx
- 文档编号:29895797
- 上传时间:2023-08-03
- 格式:DOCX
- 页数:49
- 大小:63.38KB
第七章c++程序设计.docx
《第七章c++程序设计.docx》由会员分享,可在线阅读,更多相关《第七章c++程序设计.docx(49页珍藏版)》请在冰豆网上搜索。
第七章c++程序设计
第七章异常及其处理
(exceptionanditshandling)
7.1程序运行错误及其处理
程序在运行中可能出错,例如在除法运算中用0作除数、实数运算中求负数的开方根、在计算机监控系统中所采集的数据越限,等等。
当程序出错时,一般要求用户进行处理,如重新输入数值或给出新的控制量等。
必要时则中断程序。
程序首先应检测到出错,然后才能提示用户进行处理或由程序自动进行处理。
传统的错误处理方式唯有依靠函数返回提示错误用的局部标志数据或者全局标志数据来提示出错(全局数据的安全性差,极易受到破坏)。
这类做法的困难是程序只能在函数返回主程序后才能检查标志数据的提示内容。
错误处理的一种新的方式是使用“异常”(exception)。
异常处理是由程序设计语言提供的运行时刻错误处理的一种方式。
一旦程序出现错误,随即引发和抛出“异常”,程序能在一处或多处合适的地方自动地捕获异常并进行必要的处理。
[例1]求负数的开方根时传统的错误处理方式:
//sqrt_negative_1.cpp
//erroroccurswhenthesquarerootofanegativenumberiscalculated
//systemsubroutineused
#include
#include
doublesqrt(double);
#include
voidexit(int);
doublesqroot(doublenumber)
{
if(number<0)
{
cout<<"Error!
negativeinputnumber:
"< cout<<"Programterminated! "<<'\n'; exit(-1); } returnsqrt(number);//systemsubroutine } voidmain() { cout<<"Sqrtof1.5129is"< cout<<"Sqrtof-4is"< cout<<"Sqrtof16is"< } /*Results: Sqrtof1.5129is1.23 Error! negativeinputnumber: -4 Programterminated! */ 以上程序中,当准备求取负数的平方根时,就出错,程序非正常结束。 为使程序非正常结束,须要调用系统子程序exit(),其函数原型在头文件stdlib.h内。 其中主函数中第二句运行过程中,因调用函数sqroot(-4)而导致程序意外中断。 但在sqroot(-4)之前的字符串"Sqrtof-4is"却没有显示。 这类现象将在以后众多程序中出现。 它是由于输出语句的整体性所决定的。 请见第八章§8.1.2.2“非缓冲输出流”中的详细解释。 [例2]第五章§5.3.1.3“除法运算符重载”中的[例1] 该程序中的子程序 doubledivision: : operator/(doubledenominator) { if(denominator==0) { cout<<"attemptedtodividebyzero"< return-999999; } return(numerator/denominator); } 将-999999的返回值作为错误标志。 voidmain() { doublenumerator,denominator; doubleresult; divisionnum(numerator); result=num/denominator; //i.e.result=num.operator/(denominator) if(result==-999999) { cout<<"Thequotientismeaningless! "< cout<<"Tryagain! "< } …… } 主函数main()在子函数doubledivision: : operator/(doubledenominator)返回主函数后立即检查返回值,如发现出错,就加以显示。 如果程序中出错地方较多,这类处理方法就有其局限性: 当调用函数的级联数量较多(即一个函数调用下一个函数,而下一个又调用再下一个的级联方式)而又需要在其它地方(例如上级的上级的上级)统一进行处理时,逐级传递错误码太繁琐。 因此须要借助于异常处理。 请参阅后面本章§7.2.1“抛出异常”中的[例3]中的程序exception_5.cpp。 [例3]依靠异常来处理求负数开方根的例子(当然此例中并不一定需要异常处理): //exception_sqrt_1.cpp //exceptionoccurswhenthesquarerootofanegativenumberiscalculated #include #include doublesqrt(double); //#include doublesqroot(doublenumber) { if(number<0)thrownumber;//exceptionobject returnsqrt(number);//systemsubroutine } voidmain() { try{ cout<<"Sqrtof1.5129is"< cout<<"Sqrtof-4is"< cout<<"Sqrtof16is"< } catch(double) {//exceptionhandler cout<<"Error! negativeinputnumber"<<'\n'; cout<<"Programterminated! "<<'\n'; //exit(-1);//因已到程序结尾,故不再需要此函数} } /*Results: Sqrtof1.5129is1.23 Error! negativeinputnumber Programterminated! */ 一种较好的编程方式是首先编写正常运行的代码,然后再添加用于处理各类异常情况的语句。 7.2异常及其处理 7.2.1抛出异常 从上节[例2]中可以看出,当监测到程序出错时,就抛出异常。 其格式为: try {…… 此处监测到(或在被调用的程序中监测到)程序出错时,throw异常对象; …… } 其中被抛出的异常对象可以是预定义数据类型(例如int、double、char*等)的变量及其指针和引用,但更经常使用的是各种异常类的对象及其指针和引用。 使用这些异常类对象的优点是能够提供更多关于程序错误的信息,包括处理各类错误的方法。 这些异常类可以是系统所提供的,更多情况下可以是用户自己定义的。 应该注意: 此处要求一定在try程序块中或它所调用的函数中抛出异常。 以下是不在try程序块中而在try程序块所调用的函数中抛出异常的例子: [例1]在try程序块所调用的函数quotient()中抛出异常。 //exception_1.cpp //Asimpleexceptionhandlingexample. //Checkingforadivide-by-zeroexception //andthrowinganintetgerasanexception #include //Definitionoffunctionquotient.Demonstratesthrowing //anexceptionwhenadivide-by-zeroexceptionisencountered. doublequotient(doublenumerator,doubledivisor) { if(divisor==0)throwdivisor; //被抛出的异常对象是预定义类型即double型数据 returnnumerator/divisor; } //Driverprogram voidmain() { //thetryblockwrapsthecodethatmaythrowan //exceptionandthecodethatshouldnotexecute //ifanexceptionoccurs try{ cout<<"Thequotientof18/9is"< cout<<"Thequotientof18/0is"< cout<<"Thequotientof4.5/9is"< } catch(doubledd) {//exceptionhandler cout<<"Exception: divisor"< "< } } /*Results: Thequotientof18/9is2 Exception: divisor0used! */ 从以上例子中可以看出,当程序不出错时,程序按照顺序控制结构逐条语句地向下执行,并返回除法运算结果。 而当程序出错时(即当除数为零时),程序就在抛出异常处中断,不再执行以后的语句(不执行第三句),并跳转至catch程序块再继续往下执行程序。 [例2]使用系统的异常类classexception,为此,须包含头文件 //exception_3.cpp //sameasexception_1.cpp,onlydifferingin //throwinganobjectofaclassinsteadofadoublevalue #include #include //Definitionoffunctionquotient.Demonstratesthrowing //anexceptionwhenadivide-by-zeroexceptionisencountered. doublequotient(doublenumerator,doubledivisor) { if(divisor==0)throwexception(); //被抛出的异常对象是异常类即classexception的对象, //classexceptionisdeclaredintheincludefile returnnumerator/divisor; } //Driverprogram voidmain() { try{ cout<<"Thequotientof18/9is"< cout<<"Thequotientof18/0is"< cout<<"Thequotientof4.5/9is"< } catch(exceptionex) {//exceptionhandler cout< } } /*Results: Thequotientof18/9is2 Unknownexception*/ 以上程序中classexception是系统所提供的异常类,在抛出异常时建立该异常类exception的对象。 为使用classexception,在程序中应包含头文件 catch程序块中内容将在下一小节§7.2.2中介绍。 以上程序中,当出现异常时,抛出异常,程序流即自动退出try程序块,进入catch程序块,进行处理后即进入catch程序块以后的语句,不再继续执行try程序块中抛出异常语句后的其他语句(上例中不再执行第三条语句“……quotient(4.5,9)……”)。 以上程序中无法显示异常内容,但只需将以上程序略加修改,即可显示异常内容。 如exception_4.cpp(未显示整个程序)中,只需将 doublequotient(intnumerator,intdivisor)函数的第一句 “if(divisor==0)throwexception();” 改为: “if(divisor==0) throwexception("Divide-by-zeroexception"); //使用系统的异常类的对象并初始化” 即可! 也就是在建立classexception的对象时,使用字符串"divide-by-zeroexception"将该异常对象的字符串数据初始化,输入用于显示异常性质的字符串。 在调用异常对象中的what()函数时就能显示该字符串。 这就获得如下更明确的运行结果: /*Results: Thequotientof18/9is2 Divide-by-zeroexception*/ 以前提到过,当函数级联调用时,使用显示出错的函数返回值,要逐级向上递送,很不方便。 而使用异常处理就方便了。 [例3]函数级联调用时的异常处理 //exception_5.cpp //cascadedinvocationofsub-routines #include #include //Definitionoffunctionquotient.Demonstratesthrowing //anexceptionwhenadivide-by-zeroexceptionisencountered. doublequotient(doublenumerator,doubledivisor) { if(divisor==0)throwexception("Divide-by-zeroexception"); //使用系统的异常类 returnnumerator/divisor; } doubleh(doublenumerator,doubledivisor) { doubleresult=quotient(numerator,divisor); cout<<"functionh()inreturnpath! "< returnresult; } doubleg(doublenumerator,doubledivisor) { doubleresult=h(numerator,divisor); cout<<"functiong()inreturnpath! "< returnresult; } doublef(doublenumerator,doubledivisor) { doubleresult=g(numerator,divisor); cout<<"functionf()inreturnpath! "< returnresult; } //Driverprogram voidmain() { try{ cout<<"Thequotientof18/9is"< cout<<"Thequotientof18/0is"< cout<<"Thequotientof4.5/9is"< } catch(exceptionex) {//exceptionhandler cout< } } /*Results: functionh()inreturnpath! functiong()inreturnpath! functionf()inreturnpath! Thequotientof18/9is2 Divide-by-zeroexception */ 以上程序中正常调用函数的路径是: main()->f()->g()->h()->quotient(),在函数返回时则采取相反路径,即quotient()->h()->g()->f()->main()。 但在出现异常时,也即调用主函数中以下语句 cout<<"Thequotientof18/0is"< 时,就不再按步就班,而是直接从quotient()返回至main()中的catch程序块。 其流程如下图所示: 正常运行流程引发异常流程 h() 其中 表示函数调用路径 表示正常返回路径 表示出现异常时的返回路径 7.2.2捕获异常 捕获异常以便进行处理时,通常使用catch程序块,其格式为: catch(数据或类的对象) {程序块} 可在该程序块中显示相关信息和进行必要处理。 [例1]使用异常检查整型数组下标越限 //exception_7.cpp //使用异常检查整型数组下标越限 #include intarr[]={1,2,3,4,5,6,7,8,9,10}; intLEN;//numberofelements int&check(inti) { if((i>=LEN)||(i<0))throwi; returnarr[i]; } voidmain() { //Numberofelementsisdeterminedfirst LEN=sizeof(arr)/sizeof(arr[0]); try{ cout<<"arr[1]="< (1)< cout<<"arr[4]="< cout<<"arr[-2]="< } catch(intx) {cout<<"Exception: subscript"< <<"beyondlimits(numberofelements="< } } /*Results: arr[1]=2 arr[4]=5 Exception: subscript-2beyondlimits(numberofelements=10) */ 以上程序中,主程序首先自动地确定数组单元数。 无论该数组类型如何,都可正确地求得。 当下标越限时,就抛出异常。 稍后在catch程序块中捕获异常,并显示该下标的数值。 以上是捕获一个异常的例子,以下是捕获两个异常并作不同显示的例子: [例2]输入两个数值,两者相除,求其商。 如分母为零,抛出第一类异常。 如错误地输入错误类型的变量例如字符变量,则抛出第二类异常。 这两类异常由一个catch程序块捕获,而能作不同显示处理。 //exception_6.cpp //differentexceptionscaughtbyoneblock #include #include doublequotient(doublenumerator,doubledivisor) { if(divisor==0) throwexception("Divide-by-zeroexception"); //使用系统的异常类并初始化其对象 returnnumerator/divisor; } voidmain() { doublenumerator,divisor; cout<<"Entertwovalues: "; cin>>numerator>>divisor; try{ if(! cin)throwexception("Wronginputtypeexception"); cout<<"Thequotientof"< < cout<<"Thequotientof4.5/9is"< } catch(exceptionex) {//exceptionhandler cout< } }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第七 c+ 程序设计