20009-3-241面向对象程序设计(下)课程讲义——C++任课教师:叶亚琴所在院系:信息工程学院软件工程系任课时间:2010年3月-2010年4月20009-3-242第12章异常处理什么是异常处理对运行时出现的差错以及其他例外情况的处理。异常处理的基本思想错误对于编程人员可以预料到但却无法避免发现错误的函数又并不具备处理错误的能力C++异常处理机制:异常引发与处理可不在同一位置和层次。好处:底层函数专注解决算法;上层负责后勤、善后工作20009-3-243第12章异常处理有哪些异常需要处理系统异常(内存问题、指针问题等)自定义异常:如分母为0时、指针为空时、…异常处理的实质:(举一个例子说明)在合适的地方处理if(){…}20009-3-244第12章异常处理——异常处理方法方法一:在函数中设置处理异常的程序段。缺点:会使程序过于复杂和庞大。方法二:发出一个异常信息传给它的上一级。上级捕捉到这个信息后进行处理。特点:效率高,程序代码简洁。20009-3-245第12章异常处理——C++异常处理机制由检查(try)、抛出(throw)和捕捉(catch)三部分构成。注意:把需要检查的语句放在try块中;throw用来当出现异常时发出一个异常信息;而catch则用来捕捉异常信息,如果捕捉到了异常信息,就处理它。20009-3-246throw语句一般是由throw运算符和一个数据组成的,其形式为:throw表达式;try-catch的结构为:try{被检查的语句}catch(异常信息类型[变量名]){进行异常处理的语句}第12章异常处理——C++异常处理机制20009-3-247第12章异常处理——C++异常处理流程(1)首先把可能出现异常的、需要检查的语句或程序段放在try后面的花括号中。(2)程序开始运行后,按正常的顺序执行到try块,开始执行try块中花括号内的语句。如果在执行try块内的语句过程中没有发生异常,则catch子句不起作用,流程转到catch子句后面的语句继续执行。(3)如果在执行try块内的语句(包括其所调用的函数)过程中发生异常,则throw运算符抛出一个异常信息。throw抛出异常信息后,流程立即离开本函数,转到其上一级的函数(main函数)。20009-3-248在VC++6.0环境中,为了使用异常处理机制,需要进行以下设置:打开ProjectSettings对话框。选择C/C++选项卡。在Category栏中选择C++language。选择EnableExceptionHandling。第12章异常处理——VC++设置20009-3-249例处理分母为零的异常#includeiostream.hintDiv(intx,inty);intmain(){try{cout5/2=Div(5,2)endl;cout8/0=Div(8,0)endl;cout7/1=Div(7,1)endl;}catch(int){coutexceptofdevidingzero.\n;}coutthatisok.\n;return0;}intDiv(intx,inty){if(y==0)throwy;returnx/y;}程序运行结果如下:5/2=2exceptofdevidingzero.thatisok.10#includeiostream.hvoidmain(){char*ptr;try//异常模块{if((ptr=newchar[64*1024])==NULL)throwNotEnoughMemory!;}catch(char*str)//异常错误处理模块{//……错误处理代码coutException:strendl;}}例处理内存分配的异常20009-3-2411例14.1给出三角形的三边a,b,c,求三角形的面积。只有a+bc,b+ca,c+ab时才能构成三角形。设置异常处理,对不符合三角形条件的输出警告信息,不予计算。先写出没有异常处理时的程序:#includeiostream#includecmathusingnamespacestd;intmain(){doubletriangle(double,double,double);doublea,b,c;cinabc;while(a0&&b0&&c0){couttriangle(a,b,c)endl;cinabc;}return0;}20009-3-2412doubletriangle(doublea,doubleb,doublec){doublearea;doubles=(a+b+c)/2;area=sqrt(s*(s-a)*(s-b)*(s-c));returnarea;}运行情况如下:654↙(输入a,b,c的值)9.92157(输出三角形的面积)11.52↙(输入a,b,c的值)0.726184(输出三角形的面积)121↙(输入a,b,c的值)0(输出三角形的面积,此结果不对,不是三角形)106↙(输入a,b,c的值)(结束)20009-3-2413修改程序,在函数traingle中对三角形条件进行检查,如果不符合三角形条件,就抛出一个异常信息,在主函数中的try-catch块中调用traingle函数,检测有无异常信息,并作相应处理。修改后的程序如下:#includeiostream#includecmathusingnamespacestd;voidmain(){doubletriangle(double,double,double);doublea,b,c;cinabc;try//在try块中包含要检查的函数{while(a0&&b0&&c0){couttriangle(a,b,c)endl;cinabc;}}catch(double)//用catch捕捉异常信息并作相应处理{couta=a,b=b,c=c,thatisnotatriangle!endl;}coutendendl;}doubletriangle(doublea,doubleb,doublec)//三角形面积{doubles=(a+b+c)/2;if(a+b=c||b+c=a||c+a=b)throwa;//抛出异常信息returnsqrt(s*(s-a)*(s-b)*(s-c));}20009-3-2414第12章异常处理——C++异常处理机制(1)被检测的函数必须放在try块中,否则不起作用。(2)try块和catch块作为一个整体出现。(3)try和catch块中必须用花括号括起来。(4)try-catch结构中只能有一个try块,却可以有多个catch块,以便与不同的异常信息匹配。(5)catch后一般只写异常的类型,如:catch(double)(6)catch(…)可以捕捉任何类型的异常信息,如catch(…){;}(7)trycatch结构可以与throw出现在同一个函数中,也可以不在同一函数中。找:本函数→最近的(8)throw将当前处理的异常信息再次抛出→上一层(9)如throw抛出的异常找不到与之匹配的catch块,系统就会调用系统函数terminate,程序终止。20009-3-2415例14.2在函数嵌套的情况下检测异常处理。这是一个简单的例子,用来说明在try块中有函数嵌套调用的情况下抛出异常和捕捉异常的情况。请自己先分析以下程序。#includeiostreamusingnamespacestd;intmain(){voidf1();try{f1();}//调用f1()catch(double){coutOK0!endl;}coutend0endl;return0;}voidf1(){voidf2();try{f2();}//调用f2()catch(char){coutOK1!;}coutend1endl;}voidf2(){voidf3();try{f3();}//调用f3()catch(int){coutOk2!endl;}coutend2endl;}voidf3(){doublea=0;try{throwa;}//抛出异常catch(float){coutOK3!endl;}coutend3endl;}运行结果:OK0!end020009-3-2416catch(double)catch(char)catch(int)catch(float)doublea;(1)函数嵌套时异常处理示意图20009-3-2417(2)如果将f3函数中的catch子句改为catch(double),而程序中其他部分不变,则程序运行结果如下:OK3!(在f3函数中捕获异常)end3(执行f3函数中最后一个语句时的输出)end2(执行f2函数中最后一个语句时的输出)end1(执行f1函数中最后一个语句时的输出)end0(执行主函数中最后一个语句时的输出)(3)如果在此基础上再将f3函数中的catch块改为catch(double){coutOK3!endl;throw;}程序运行结果如下:OK3!(在f3函数中捕获异常)OK0!(在主函数中捕获异常)end0(执行主函数中最后一个语句时的输出)20009-3-2418在throw抛出异常信息被catch捕获时,先对有关的局部对象进行析构(调用类对象的析构函数);析构对象的顺序与构造的顺序相反;这包括将从对应的try块开始到异常被抛掷处之间构造且尚未析构的所有自动对象进行析构。析构的顺序与构造的顺序相反。程序会从最后的一个catch处理之后开始恢复执行。第12章异常处理——异常中处理析构函数20009-3-2419例14.3在异常处理中处理析构函数。这是一个为说明在异常处理中调用析构函数的示例,为了清晰地表示流程,程序中加入了一些cout语句,输出有关的信息,以便对照结果分析程序。#includeiostream#includestringusingnamespacestd;classStudent{public:Student(intn,stringnam)//定义构造函数{coutconstructor-nendl;num=n;name=nam;}~Student(){coutdestructor-numendl;}//析构函数voidget_data();//成员函数声明private:intnum;stringname;};20009-3-2420voidStudent::get_data()//定义成员函数{if(num==0)thrownum;//如num=0,抛出int型elsecoutnum“”nameendl;//若num≠0,输出cout“inget_data()”endl;//输出信息}voidfun(){Studentstud1(1101,Tan);//建立对象stud1stud1.get_data();//调用stud1的get_data函数Studentstud2(0,Li);//建立对象stud2stud2.get_data();//调用stud2的get_data函数}intmain(){coutmainbeginendl;coutcallfun()endl;try{fun();}catch(intn){coutnum=n,error!endl;}coutmainendendl;return0;}运行结果如下:mainbegincallfun()constructor-11011101taninget_data()constructor-0destructor-