《c基础知识10异常处理机制》由会员分享,可在线阅读,更多相关《c基础知识10异常处理机制(40页珍藏版)》请在金锄头文库上搜索。
1、第十章第十章 异常处理异常处理大型和十分复杂的程序往往会产生一些很难查找的甚至是大型和十分复杂的程序往往会产生一些很难查找的甚至是无法避免的运行时错误。无法避免的运行时错误。当发生运行时错误时,不能简单地结当发生运行时错误时,不能简单地结束程序运行,而是退回到任务的起点,指出错误,并由用户决束程序运行,而是退回到任务的起点,指出错误,并由用户决定下一步工作定下一步工作。面向对象的异常处理(面向对象的异常处理(exception handling)机制是机制是C+语言用以解决这个问题的有力工具。语言用以解决这个问题的有力工具。 函数执行时,放在函数执行时,放在try(测试)程序块(测试)程序块中
2、的任何类型的数中的任何类型的数据对象发生异常,都可被据对象发生异常,都可被throw表达式表达式抛出,随即抛出,随即逆调用链退逆调用链退回回,直到被,直到被catch子句子句捕获,并在此执行捕获,并在此执行异常处理异常处理,报告出现,报告出现的异常等情况。从抛出到捕获,应将各嵌套调用函数残存在的异常等情况。从抛出到捕获,应将各嵌套调用函数残存在栈中的自动对象、自动变量和现场保护内容等进行清除。如栈中的自动对象、自动变量和现场保护内容等进行清除。如果已退到入口函数还未捕获则由果已退到入口函数还未捕获则由terminate()函数来终结入口函数来终结入口函数。函数。俱懒玖侵晴揩匆招磋妒咎拭噬兆食啮
3、斜斯党占役卢牧氰蚊闸计氧宝锁馈阅c+基础知识10异常处理机制c+基础知识10异常处理机制第十章第十章 异常处理异常处理10.1 异常的概念异常的概念 10.3 栈展开与栈展开与 异常异常捕获捕获 10.2 异常处理的机制异常处理的机制 10.5异常和继承异常和继承 10.7 C+标准库异常类标准库异常类 层次结构层次结构 (选读)(选读)10.6异常规范(异常规范(选读选读) 10.4 异常的重新抛出异常的重新抛出 和和catch_all子句子句 赶践燕亏洋晴滓揖野呀盒瞧诫抚宁玉泰樱避乍住肥些韵改哺焚翠增钎戮鲤c+基础知识10异常处理机制c+基础知识10异常处理机制10.1 异常的概念异常的概
4、念 异常概念的引入:异常概念的引入: 异常(异常(exception)是程序可能检测到)是程序可能检测到 的,运行时不正常的情况的,运行时不正常的情况,如存储空间耗尽、数组越,如存储空间耗尽、数组越界、被界、被0除等等。可以预见可能发生在什么地方,但是无法确除等等。可以预见可能发生在什么地方,但是无法确知怎样发生和何时发生。特别在一个大型的程序(软件)中,知怎样发生和何时发生。特别在一个大型的程序(软件)中,程序各部分是由不同的小组编写的,它们由公共接口连起来,程序各部分是由不同的小组编写的,它们由公共接口连起来,错误可能就发生在相互的配合上,也可能发生在事先根本想不错误可能就发生在相互的配合
5、上,也可能发生在事先根本想不到的个别的条件组合上。到的个别的条件组合上。C+提供了一些内置的语言特性来产生(提供了一些内置的语言特性来产生(raise)或抛出)或抛出(throw)异常,用以通知)异常,用以通知“异常已经发生异常已经发生”,然后由预先安,然后由预先安排的程序段来捕获(排的程序段来捕获(catch)异常,并对它进行处理。这种机)异常,并对它进行处理。这种机制可以在制可以在C+程序的两个无关(往往是独立开发)的部分进行程序的两个无关(往往是独立开发)的部分进行“异常异常”通信。由程序某一部分引发了另一部分的异常,这一通信。由程序某一部分引发了另一部分的异常,这一异常可回到引起异常的
6、部分去处理(逆着程序函数的调用链)。异常可回到引起异常的部分去处理(逆着程序函数的调用链)。租蚌曙坏圭己籍枪渝噎攒尸装喘斌揉抢哩猖涯绸吧伙犁祭舵禄悦蝉瘴坍访c+基础知识10异常处理机制c+基础知识10异常处理机制10.2 异常处理的机异常处理的机制制测到栈满或空就抛出一个异常。测到栈满或空就抛出一个异常。template void Stack:Push(const T&data) if(IsFull() throw pushOnFull(data); /注意加了括号注意加了括号,是构造一个无名对象是构造一个无名对象 elements+top=data; templateT Stack:Pop(
7、) if(IsEmpty() throw popOnEmpty(); return elementstop-; 注意注意pushOnFull是是类类,C+要求抛出的必须是对象要求抛出的必须是对象,所以必,所以必须有须有“()”,即,即调用构造函数建立一个对象调用构造函数建立一个对象。异常与异常抛出:异常与异常抛出:以栈为例,异常类声明如下以栈为例,异常类声明如下:class popOnEmpty.; /栈空异常栈空异常class pushOnFull.; /栈满异常栈满异常弟庶绦肤游抱筛旬蚊锈舔妇括鞍擦竖受晦度差痘腰柜皖碴塘轿屎缆欺茫杏c+基础知识10异常处理机制c+基础知识10异常处理机制1
8、0.2 异常处理的机异常处理的机制制throw表达式表达式抛出异常抛出异常为异常处理的为异常处理的第一步第一步。在堆栈的压在堆栈的压栈和出栈操作中发生错误而抛出的异常,理所当然地应由栈和出栈操作中发生错误而抛出的异常,理所当然地应由调用堆栈的程序来处理。调用堆栈的程序来处理。异常并非总是类对象,异常并非总是类对象,throw表表达式也可以抛出任何类型的对象,如枚举、整数等等。但达式也可以抛出任何类型的对象,如枚举、整数等等。但最常用的是类对象。最常用的是类对象。在在C+中异常抛出与异常处理之间有一整套程序设计的机制。中异常抛出与异常处理之间有一整套程序设计的机制。首先采用关键字首先采用关键字t
9、ry,构成一个,构成一个try块(块(try block),它包含,它包含了了抛出异常抛出异常的语句。当然也可以是包含了这样的调用语句,的语句。当然也可以是包含了这样的调用语句,该语句所调用的函数中有能够抛出异常的语句。该语句所调用的函数中有能够抛出异常的语句。异常处理机制:儿箔堆圭兜沃国祝吊趴沈汁渔缘瓮献踩殿烛棋冬志吼牌蛔塘配录柱喝龋邹c+基础知识10异常处理机制c+基础知识10异常处理机制10.2 异常处理的机制异常处理的机制int main() int a9=1,2,3,4,5,6,7,8,9,b9=0,i; stackistack(8); try for(i=0;i9;i+) ista
10、ck.Push(ai); istack.PrintStack(); catch(pushOnFull)cerr”栈满栈满”endl; try for(i=0;i9;i+)bi=istack.Pop(); catch(popOnEmpty)cerr”栈空栈空”endl; for(i=0;i9;i+) coutbit; coutendl; return 0; try块与块与catch子句的关系实例:子句的关系实例:些傀植卓箭派案舱匠醇删炔住轨疑江陆动骆殷忻甭盏函弧杭砸韵玫胞毡皿c+基础知识10异常处理机制c+基础知识10异常处理机制10.2 异常处理的机制异常处理的机制由由catch字句捕获并处理
11、异常是字句捕获并处理异常是第二步第二步。注意与。注意与catch语句语句分别分别匹配匹配的是在压栈和出栈成员函数模板中的的是在压栈和出栈成员函数模板中的throw语句,语句,一个抛出一个抛出pushOnFull类的无名对象,另一个抛出类的无名对象,另一个抛出popOnEmpty类的无名对象。类的无名对象。在编制程序时有一条在编制程序时有一条惯例惯例:把正常执行的程序与异常处理两:把正常执行的程序与异常处理两部分分隔开来,这样使代码更易于跟随和维护。在上例中,部分分隔开来,这样使代码更易于跟随和维护。在上例中,我们可以把两个我们可以把两个try块合成一个,而把两个块合成一个,而把两个catch子
12、句都放在子句都放在函数最后。函数最后。 说明:说明:这里有两个这里有两个try块,分别对应压栈与出栈;也有两块,分别对应压栈与出栈;也有两个个catch子句(子句(catch clause),分别处理压栈),分别处理压栈时的栈满和出栈时的栈空。时的栈满和出栈时的栈空。 扇咒戴弛降霉桓硅唯鸟顶跟哭矢汪哲盈萄河裁赃这陛付指格受藤涪妓桂铀c+基础知识10异常处理机制c+基础知识10异常处理机制10.2 异常处理的机制异常处理的机制1如如果果没没有有异异常常发发生生,继继续续执执行行try块块中中的的代代码码,与与try块块相相关联关联 的的catch子句被忽略,程序正常执行,子句被忽略,程序正常执行
13、,main()返回返回0。2当当第第一一个个try块块在在for循循环环中中抛抛出出异异常常,则则该该for循循环环退退出出,try块块 也也 退退 出出 , 去去 执执 行行 可可 处处 理理 pushOnFull异异 常常 的的 catch子子 句句 。 istack.PrintStack()不再执行,被忽略。不再执行,被忽略。3如如果果第第二二个个try块块调调用用Pop()抛抛出出异异常常,则则退退出出for和和try块块,去执行可处理去执行可处理popOnEmpty异常的异常的catch子句。子句。4当某条语句抛出异常时,跟在该语句后面的语句将被跳过。当某条语句抛出异常时,跟在该语句
14、后面的语句将被跳过。程序执行权交给处理异常的程序执行权交给处理异常的catch子句,如果没有子句,如果没有catch子句子句能够处理异常,则交给能够处理异常,则交给C+标准库中定义的标准库中定义的terminate()。流程控制规则:流程控制规则:帘念斜不届堆虞窑费圆问崩逐遥券惮祷营藉乾娜版弃靠车供挫札挡饮激时c+基础知识10异常处理机制c+基础知识10异常处理机制10.3 栈展开与异常捕获栈展开与异常捕获catch子句由三部分组成:关键字子句由三部分组成:关键字catch、圆括号中的、圆括号中的异异常声明常声明以及复合语句中的以及复合语句中的一组语句一组语句。catch子句不是函数,所以圆括
15、号中不是形参,而是一个子句不是函数,所以圆括号中不是形参,而是一个异常类型声明,可以是类型也可以是对象。异常类型声明,可以是类型也可以是对象。catch子句的使用:它只有一个子句,没有定义和调用之子句的使用:它只有一个子句,没有定义和调用之分。使用时由系统按规则自动在分。使用时由系统按规则自动在catch子句列表中匹配。子句列表中匹配。 catch子句可以包含返回语句(子句可以包含返回语句(return),也可不包含返),也可不包含返回语句。回语句。包含返回语句,则整个程序结束包含返回语句,则整个程序结束。而不包含返回。而不包含返回语句,则执行语句,则执行catch列表之后的下一条语句。列表之
16、后的下一条语句。 catch子句子句说明:说明:当当try块中的语句抛出异常时,系统通过查看跟在块中的语句抛出异常时,系统通过查看跟在其后的其后的catch子句列表,来查找可处理该异常的子句列表,来查找可处理该异常的catch子句。子句。火凯佩牡悲障葱早潦煌栓追连挤纲处舷啦酣朔威严说勉醋办破纬椰饲凉吧c+基础知识10异常处理机制c+基础知识10异常处理机制10.3 栈展开与异常捕获栈展开与异常捕获对应在对应在throw表达式中,构造抛出对象也要有实参:表达式中,构造抛出对象也要有实参:throw pushOnFull(data); /data即即Push(const &data)中的参数中的参
17、数datatemplate class pushOnFull T _value;public: pushOnFull(T i):_value(i) /或写为或写为pushOnFull(T i)_value=i; T value()return _value; ;新的私有数据成员新的私有数据成员_value保存那些不能被压入栈中的值。该保存那些不能被压入栈中的值。该值即调用构造函数时的实参。值即调用构造函数时的实参。catchcatch子句异常声明探讨:子句异常声明探讨:异常声明中可以是一个对象声明。以栈为例。当栈满异常声明中可以是一个对象声明。以栈为例。当栈满时,要求在异常对象中时,要求在异常
18、对象中保存不能被压入到栈中的值保存不能被压入到栈中的值,pushOnFull类可定义如下:类可定义如下:斌腾充寐庭歪何频少弟蔷媚潭疆炕形啄蚂聂扰催惭栓漳莆项得唁靠玉靠痔c+基础知识10异常处理机制c+基础知识10异常处理机制10.3 栈展开与异常捕获栈展开与异常捕获 在在catch子句中,要取得子句中,要取得_value,须调用,须调用pushOnFull 中的成员函数中的成员函数value():catch(pushOnFull eObj) cerr”栈满栈满”eObj.value()”未压入栈未压入栈”endl; return 1;在在catch子句的异常声明中声明了对象子句的异常声明中声明
19、了对象eObj,用它来调用,用它来调用pushOnFull类的对象成员函数类的对象成员函数value()。异常对象是在抛出。异常对象是在抛出点被创建,与点被创建,与catch子句是否显式要求创建一个异常对象无关,子句是否显式要求创建一个异常对象无关,该对象总是存在,在该对象总是存在,在catch子句中只是为了调用异常处理对象子句中只是为了调用异常处理对象的成员函数才声明为对象,不用类。的成员函数才声明为对象,不用类。*catch子句异常声明中采用对象只是一种形式。甚至子句异常声明中采用对象只是一种形式。甚至异常并非一个类对象时,也可以用同样的格式,比如异常并非一个类对象时,也可以用同样的格式,
20、比如异常为一枚举量,这时就等效于按值传递,而不是调异常为一枚举量,这时就等效于按值传递,而不是调用类对象的公有成员。用类对象的公有成员。 胜口舞烫噪柿澜晓烈掌预翱觉之臆贤物惨脐凭拈房底骑荔撵澈很唉厚姓然c+基础知识10异常处理机制c+基础知识10异常处理机制10.3 栈展开与异常捕获栈展开与异常捕获catch子子句句的的异异常常声声明明与与函函数数参参数数声声明明类类似似,可可以以是是按按值值传传送送,也也可可以以是是按按引引用用传传递递。对对大大型型类类对对象象减减少少不不必必要要的的复复制制是是很很有有意意义义的的,所所以以对对于于类类类类型型的的异异常常,其异常声明最好也是被声明为引用。
21、如:其异常声明最好也是被声明为引用。如:catch(pushOnFull & eObj) cerr”栈满栈满”eObj.value()”未压栈未压栈”endl; return 1; 使用引用类型的异常声明,使用引用类型的异常声明,catch子句能够修改异常对象,子句能够修改异常对象,但仅仅是异常对象本身,正常程序部分的量并不会被修改。但仅仅是异常对象本身,正常程序部分的量并不会被修改。与一般类对象不同,实际上异常对象处理完后,生命期也就与一般类对象不同,实际上异常对象处理完后,生命期也就结束了。结束了。只有需要重新抛出异常(在下一节中讨论),修改只有需要重新抛出异常(在下一节中讨论),修改操作
22、才有意义。操作才有意义。 【例【例10.1】包含栈满或空异常的完整的程序。包含栈满或空异常的完整的程序。狗笋拌替租坯糊梯基泅谬桔奇啦族伴特皇咯倦膝哑养虑雀扑挺止莹换窖偷c+基础知识10异常处理机制c+基础知识10异常处理机制10.3 栈展开与异常捕获栈展开与异常捕获把把程程序序的的正正常常处处理理代代码码和和异异常常处处理理代代码码分分离离的的最最清清楚楚的的方方法法是是定定义义函函数数try块块(Function try Block)。这这种方法是把整个函数包括在种方法是把整个函数包括在try块中。块中。 一一个个函函数数try块块把把一一组组catch子子句句同同一一个个函函数数体体相相关
23、关联联。如如果果函函数数体体中中的的语语句句抛抛出出一一个个异异常常,则则考考虑虑跟跟在在函函数数体体后后面面的的处处理理代代码来处理该异常。函数码来处理该异常。函数try块对构造函数尤其有用。块对构造函数尤其有用。【例【例10.1_1】定义函数定义函数try块(块(Function try Block)。)。 函数函数trytry块的使用:块的使用:撬编泌与可召悬愤睛际隘担损丰踞抱侥泉慰疑锻搂惧生痕捞吠惺件踪脸菱c+基础知识10异常处理机制c+基础知识10异常处理机制寻找匹配的寻找匹配的catch子句有固定子句有固定的过程:如果的过程:如果throw表达式位表达式位于于try块中,则检查与块
24、中,则检查与try块相块相关联的关联的catch子句列表,看是子句列表,看是否有一个子句能够处理该异常,否有一个子句能够处理该异常,有匹配的,则该异常被处理;有匹配的,则该异常被处理;找不到匹配的找不到匹配的catch子句,则子句,则在主调函数中继续查找。如果在主调函数中继续查找。如果一个函数调用在退出时带有一一个函数调用在退出时带有一个被抛出的异常未能处理,而个被抛出的异常未能处理,而且这个调用位于一个且这个调用位于一个try块中,块中,则检查与该则检查与该try块相关联的块相关联的catch子句列表,看是否有一子句列表,看是否有一个子句匹配,有,则处理该异个子句匹配,有,则处理该异常;没有
25、,则查找过程在该函常;没有,则查找过程在该函数的主调函数中继续进行。即数的主调函数中继续进行。即这个查找过程逆着嵌套的函数这个查找过程逆着嵌套的函数调用链向上继续,直到找到处调用链向上继续,直到找到处理该异常的理该异常的catch子句。子句。只要只要遇到第一个匹配的遇到第一个匹配的catch子句,子句,就会进入该就会进入该catch子句,进行子句,进行处理,查找过程结束。处理,查找过程结束。 瀑策醉议劝练玲舞赐喝书鸿伯汝绦靶都鸥拿激湿啪韦奏蛊噎扭离郊瑞恭豹c+基础知识10异常处理机制c+基础知识10异常处理机制10.3 栈展开与异常捕获栈展开与异常捕获在栈异常处理的例子中,对在栈异常处理的例子
26、中,对popOnEmpty,首先应在,首先应在istack的成员函数的成员函数Pop()中找,因为中找,因为Pop()中没有中没有try块,不存块,不存在在catch子句,所以子句,所以Pop()带着一个异常退出。下一步是检查带着一个异常退出。下一步是检查调用调用Pop()的函数,这里是的函数,这里是main(),在,在main()中对中对Pop()的调的调用位于一个用位于一个try块中,则可用与该块中,则可用与该try块关联的块关联的catch子句列表子句列表中的某一个来处理,找到第一个中的某一个来处理,找到第一个popOnEmpty类型异常声明类型异常声明的的catch子句,并进入该子句进
27、行异常处理。子句,并进入该子句进行异常处理。 栈展开:栈展开: 因发生异常而逐步退出复合语句和函数定义的过程,被称因发生异常而逐步退出复合语句和函数定义的过程,被称为为栈展开栈展开(stack unwinding)。这是异常处理的核心技术。这是异常处理的核心技术。异常对程序的影响通常不仅是在发生异常的那个局部范围中,异常对程序的影响通常不仅是在发生异常的那个局部范围中,而且可能逆调用链而上,甚至整个任务。因此,异常处理应该而且可能逆调用链而上,甚至整个任务。因此,异常处理应该在其对程序影响的终结处进行,甚至是在调用该任务的菜单处在其对程序影响的终结处进行,甚至是在调用该任务的菜单处进行。进行。
28、丸高毯翰请邓碟羡虎歧吩陛散沤熄鹿卉失屉搪酌银匝爹秽翱筑安讨贬铁啃c+基础知识10异常处理机制c+基础知识10异常处理机制10.3 栈展开与异常捕获栈展开与异常捕获在栈展开期间,在退出的域中有某个局部量是类对象,栈展在栈展开期间,在退出的域中有某个局部量是类对象,栈展开过程将自动调用该对象的析构函数,完成资源的释放。所以开过程将自动调用该对象的析构函数,完成资源的释放。所以C+异常处理过程本质上反映的是异常处理过程本质上反映的是“资源获取是由构造函数实资源获取是由构造函数实现,而资源释放是由析构函数完成现,而资源释放是由析构函数完成” 。采用面向对象的程序设。采用面向对象的程序设计,取得资源的动
29、作封装在类的构造函数中,释放资源的动作计,取得资源的动作封装在类的构造函数中,释放资源的动作封装在类的析构函数中,当一个函数带着未处理的异常退出时,封装在类的析构函数中,当一个函数带着未处理的异常退出时,函数中这种类对象被自动销毁,资源(包括动态空间分配的资函数中这种类对象被自动销毁,资源(包括动态空间分配的资源和打开的文件)释放。所以源和打开的文件)释放。所以由文件重构对象应该放在构造函由文件重构对象应该放在构造函数中,把对象存入文件应该放在析构函数中数中,把对象存入文件应该放在析构函数中。栈展开时资源的释放栈展开时资源的释放: :异常处理应该用于面向对象的程序设计。对非面向对象的程序异常处
30、理应该用于面向对象的程序设计。对非面向对象的程序设计如果函数动态获得过资源,因异常,这些资源的释放语句设计如果函数动态获得过资源,因异常,这些资源的释放语句可能被忽略,则这些资源将永远不会被自动释放。可能被忽略,则这些资源将永远不会被自动释放。津断讳晕即戊霜太淤艾箔冀任江危桑琢钎聚收我扮沟蜜养犀栈链起屿筷图c+基础知识10异常处理机制c+基础知识10异常处理机制10.3 栈展开与异常捕获栈展开与异常捕获 异常不能够保持在未被处理的状态。异常表示一个程异常不能够保持在未被处理的状态。异常表示一个程序不能够继续正常执行,这是非常严重的问题,如果没有序不能够继续正常执行,这是非常严重的问题,如果没有
31、找到处理代码,程序就调用找到处理代码,程序就调用C+标准库中定义的函数标准库中定义的函数terminate()。异常对象的探讨:异常对象的探讨:异常对象是在异常对象是在throw表达式中建立并抛出:表达式中建立并抛出:throw表表达式通过调用异常类的构造函数创建一个临时对象,达式通过调用异常类的构造函数创建一个临时对象,然后把这个临时对象复制到一个被称为异常对象然后把这个临时对象复制到一个被称为异常对象(exception object)的存贮区中,它保证会持续到)的存贮区中,它保证会持续到异常被处理完。异常被处理完。 撞亲垢肋周盈意戈江妄叠肯巧醚配您鄂鉴坐钳吗阉开侠姨倪雄二些半熙捣c+基础
32、知识10异常处理机制c+基础知识10异常处理机制10.3 栈展开与异常捕获栈展开与异常捕获函数调用和异常处理的区别:函数调用和异常处理的区别:建立建立函数调用函数调用所需要的全部信息在所需要的全部信息在编译编译时已经获得,而时已经获得,而异常处异常处理理机制要求机制要求运行时运行时的支持。对于普通函数调用,通过函数重载的支持。对于普通函数调用,通过函数重载解析过程,编译器知道在调用点上哪个函数会真正被调用。但解析过程,编译器知道在调用点上哪个函数会真正被调用。但对于对于异常处理,编译器不知道特定的异常处理,编译器不知道特定的throw表达式的表达式的catch子子句在哪个函数中,以及在处理异常
33、之后执行权被转移到哪儿。句在哪个函数中,以及在处理异常之后执行权被转移到哪儿。这些都在运行时刻决定,异常是随机发生的,异常处理的这些都在运行时刻决定,异常是随机发生的,异常处理的catch子句是逆调用链进行查找子句是逆调用链进行查找,这与运行时的多态,这与运行时的多态虚函虚函数也是不一样的。当一个异常不存在处理代码时,系统无法通数也是不一样的。当一个异常不存在处理代码时,系统无法通知用户,所以要有知用户,所以要有terminate()函数,它是一种运行机制,当函数,它是一种运行机制,当没有处理代码(没有处理代码(catch子句)能够匹配,被抛出的异常时由它子句)能够匹配,被抛出的异常时由它通知
34、用户。通知用户。奇锌泥魏账驹凑称揍活剐炽张醚宛伏襄断勘幢费肺泽场蜗艳悯钵色楷贩农c+基础知识10异常处理机制c+基础知识10异常处理机制10.4 异常的重新抛出和异常的重新抛出和catch_all子句(选读)子句(选读) rethrow表达式仍为:表达式仍为:throw; 但仅有一个关键字,因为但仅有一个关键字,因为异常类型在异常类型在catch语句语句中已经有了,中已经有了,不必再指明。被重新抛出的异常就是原来的异常对象。不必再指明。被重新抛出的异常就是原来的异常对象。但是重但是重新抛出异常的新抛出异常的catch子句应该把自己做过的工作告诉下一个处子句应该把自己做过的工作告诉下一个处理异常
35、的理异常的catch子句,往往要对异常对象做一定修改,以表达子句,往往要对异常对象做一定修改,以表达某些信息,因此某些信息,因此catch子句中的异常声明必须被声明为引用子句中的异常声明必须被声明为引用,这样修改才能真正做在异常对象自身中,而不是副本中。这样修改才能真正做在异常对象自身中,而不是副本中。 异常的重新抛出与连续处理:异常的重新抛出与连续处理:当当catch语句捕获一个异常后,可能不能完全处理异常,语句捕获一个异常后,可能不能完全处理异常,完成某些操作后,该异常必须由函数链中更上级的函完成某些操作后,该异常必须由函数链中更上级的函数来处理,这时数来处理,这时catch子句可以重新抛
36、出(子句可以重新抛出(rethrow)该异常,把异常传递给函数调用链中更上级的另一个该异常,把异常传递给函数调用链中更上级的另一个catch子句,由它进行进一步处理。子句,由它进行进一步处理。号推控阻全输假膝晃透坝迈湿鸦淆佐砖敢碘炔茫迸沽庭嚼雁没叉择袒弯败c+基础知识10异常处理机制c+基础知识10异常处理机制10.4 异常的重新抛出和异常的重新抛出和catch_all子句(选读)子句(选读)通用通用catch子句子句(catch_all):catch(.)代码代码*/ 任何异常都可以进入这个任何异常都可以进入这个catch子句。这里的三个点称子句。这里的三个点称为省略号。花括号中的复合语句用
37、来执行指定操作。为省略号。花括号中的复合语句用来执行指定操作。异常发生后按栈展开(异常发生后按栈展开(stack unwinding)退出,动态分配的)退出,动态分配的非非类对象类对象资源不会自动释放的,通常在资源不会自动释放的,通常在catch_all子句中释放。子句中释放。void fun1() int *res; res=new int100; /定义一个资源对象定义一个资源对象 try /代码包括使用资源代码包括使用资源res和某些可能引起异常抛出的操作和某些可能引起异常抛出的操作 /异常可能有多种异常可能有多种 catch(.) /不论是那种异常都在此释放不论是那种异常都在此释放 d
38、elete res; /释放资源对象释放资源对象res throw; /重新抛出异常重新抛出异常 delete res; /正常退出前释放资源对象正常退出前释放资源对象res;翠潭抗载艺巍猫煽倦炙导零拔然至陵捌挥妊失婪样脓癸钙扎握妙骸铬绍刃c+基础知识10异常处理机制c+基础知识10异常处理机制10.4 异常的重新抛出和异常的重新抛出和catch_all子句(选读)子句(选读) catch_all子子句句可可以以单单独独使使用用,也也可可以以与与其其它它catch子子句句联联合合使使用用。如如果果联联合合使使用用,它它必必须须放放在在相相关关catch子子句句表的最后表的最后。 catch子子
39、句句被被检检查查的的顺顺序序与与它它们们在在try块块之之后后排排列列顺顺序序相相同同,一一旦旦找找到到了了一一个个匹匹配配,则则后后续续的的catch子子句句将将不不再再检检查查,按按此此规规则则,catch_all子子句句(catch(.))处处理理表前面所列各种异常之外的异常。表前面所列各种异常之外的异常。 如如果果只只用用catch_all子子句句进进行行某某项项操操作作,则则其其他他的的操操作作应应由由catch子子句句重重新新抛抛出出异异常常,逆逆调调用用链链去去查查找找新新的的处处理理子子句句来来处处理理,不不能能在在子子句句列列表表中中再再安安排排一一个个处处理理同同一一异常的
40、子句,因为第二个子句是永远执行不到的异常的子句,因为第二个子句是永远执行不到的。通用通用catch子句的应用:子句的应用:袋砖汐甜刁宾列氟归匙料八运黑艰狸壤姓钞暴栓恤淖块压鼠匹驰钟妨要琶c+基础知识10异常处理机制c+基础知识10异常处理机制10.5 异常和继承异常和继承定义一个称为定义一个称为Excp的基类,由它来打印错误信息:的基类,由它来打印错误信息:class Excp public:void print(string msg)cerrmsgendl; ;再从该基类派生出两个异常类再从该基类派生出两个异常类:class stackExcp:public Excp.; /栈异常类的基类栈
41、异常类的基类class mathExcp:public Excp.; /数学库异常的基类数学库异常的基类进一步派生出其他进一步派生出其他异常类异常类:class popOnEmpty:public stackExcp.; /栈空退栈异常栈空退栈异常class pushOnFull:public stackExcp.; /栈满压栈异常栈满压栈异常class zeroOp:public mathExcp.; /数学库零操作异常数学库零操作异常class divideByZero:public mathExcp.; /数学库被零除异常数学库被零除异常 异常的层次结构:异常的层次结构:在在C+程序中,
42、表示异常的类通常被组成为一个组(即程序中,表示异常的类通常被组成为一个组(即如在前面各节讨论的那样)或者一个如在前面各节讨论的那样)或者一个层次结构层次结构。形成了三层结构。形成了三层结构。徒卸锑察雷收垒酪鸿斋馏亥费刚霖鼎扩佯糟调咸真砚闷季够浚闰辫辈孪卷c+基础知识10异常处理机制c+基础知识10异常处理机制10.5 异常和继承异常和继承在层次结构下,异常的抛出会有一些不同,以下做法是错的在层次结构下,异常的抛出会有一些不同,以下做法是错的:if(full() pushOnFull except(data); stackExcp *pse=&except; /pse指向的类对象为指向的类对象为
43、pushOnFull throw *pse; /抛出的异常对象的类型为抛出的异常对象的类型为stackExcp这里被创建的异常类对象是这里被创建的异常类对象是stackExcp类类型,尽管类类型,尽管pse指向一个实际类型为指向一个实际类型为pushOnFull的对象,但那是的对象,但那是一个临时对象,复制到异常对象的存储区中时创建的一个临时对象,复制到异常对象的存储区中时创建的却是却是stackExcp类的异常对象。所以该异常不能被类的异常对象。所以该异常不能被pushOnFull类型的类型的catch子句处理。子句处理。层次结构异常的抛出:层次结构异常的抛出:蕾犊兄仍龋见找赶绊韩倔四名挟戍
44、陆抄嫉疟擎酸事剔昭庞遁垢掠贞疡瓮亦c+基础知识10异常处理机制c+基础知识10异常处理机制10.5 异常和继承异常和继承在处理类类型异常时,在处理类类型异常时,catch子句的排列顺序是非常重要的。子句的排列顺序是非常重要的。catch(pushOnFull). /处理处理pushOnFull异常异常catch(stackExcp). /处理栈的其他异常处理栈的其他异常catch(Excp). /处理一般异常处理一般异常派生类类型的派生类类型的catch子句必须先出现,以确保只有在没有其他子句必须先出现,以确保只有在没有其他catch子句适用时,才会进入基类类型的子句适用时,才会进入基类类型的
45、catch子句。子句。异常异常catch子句不必是与异常最匹配的子句不必是与异常最匹配的catch子句,而是子句,而是最先最先匹配到的匹配到的catch子句子句,就是第一个遇到的可以处理该异常的,就是第一个遇到的可以处理该异常的catch子句。所以在子句。所以在catch子句列表中最特化的(匹配条件最子句列表中最特化的(匹配条件最严格的)严格的)catch子句必须先出现。子句必须先出现。catchcatch子句的排列顺序:子句的排列顺序:带做竞下咆幕而赐拾师葛涤涂吊阻吭腾剿错重石捣打羊紫地龚焙惺袱掉惧c+基础知识10异常处理机制c+基础知识10异常处理机制10.5 异常和继承异常和继承类层次结
46、构的异常同样可以重新抛出(类层次结构的异常同样可以重新抛出(rethrow),把一个),把一个异常传递给函数调用列表中异常传递给函数调用列表中更上层更上层的另一个的另一个catch子句:子句:throw;类层次结构下的异常重新抛出:类层次结构下的异常重新抛出:重新抛出的异常仍是原来的异常对象。如果程序中抛出了重新抛出的异常仍是原来的异常对象。如果程序中抛出了pushOnFull类类型的异常,而它类类型的异常,而它被基类的被基类的catch子句处理子句处理,并在其中并在其中再次被抛出再次被抛出,那么这个异常,那么这个异常仍是仍是pushOnFull类的类的异常异常,而不是其基类的异常。,而不是其
47、基类的异常。永闽景摸筛傍颊思烙揽虚独觉末角艺衫鲤像颖氖且讼蒙俗伍湍惮动磐善傻c+基础知识10异常处理机制c+基础知识10异常处理机制10.5 异常和继承异常和继承在基类在基类catch子句处理的是异常对象的基类子对象的一份副本,子句处理的是异常对象的基类子对象的一份副本,该副本只在该该副本只在该catch子句中被访问,重新抛出的是原来的异常子句中被访问,重新抛出的是原来的异常对象。这个放在异常对象存储区中的异常的生命期应该是在对象。这个放在异常对象存储区中的异常的生命期应该是在处理该异常的一系列的子句中最后一个退出时才结束,也就处理该异常的一系列的子句中最后一个退出时才结束,也就是直到这时,才
48、由异常类的析构函数来销毁它。这一系列的是直到这时,才由异常类的析构函数来销毁它。这一系列的子句是由重新抛出联系起来的。子句是由重新抛出联系起来的。【例【例10.2】异常层次结构中的虚函数。异常层次结构中的虚函数。为了调用派生类对为了调用派生类对象的虚拟函数,异常声明必须为一个象的虚拟函数,异常声明必须为一个指针或引用。指针或引用。 虚函数是类层次结构中多态性的基本手段,异常类层次结虚函数是类层次结构中多态性的基本手段,异常类层次结构中也可以定义虚拟函数。构中也可以定义虚拟函数。 伏窃寝撅辅骏挑馏霉蕴熟价铅舌洒玛锨爆患嚎督堪她器傲企主才竖钟搬够c+基础知识10异常处理机制c+基础知识10异常处理
49、机制10.6 异常规范异常规范一一个个函函数数的的异异常常规规范范的的违违例例只只能能在在运运行行时时才才能能被被检检测测出出来来。如如果果在在运运行行时时,函函数数抛抛出出了了一一个个没没有有被被列列在在它它的的异异常常规规范范中中的的异异常常时时(并并且且函函数数中中所所抛抛出出的的异异常常,没没有有在在该该函函数数内内部部处处理理)则则系系统统调调用用C+标标准准库库中中定定义义的的函函数数unexpected()。仅仅当当函函数数中中所所抛抛出出的的异异常常,没没有有在在该该函函数数内内部部处处理理,而而是是逆逆调调用用链链回回溯溯寻寻找匹配的找匹配的catch子句的时候子句的时候,异
50、常规范才起作用。异常规范才起作用。如果异常规范为如果异常规范为throw(),则表示不得抛出任何异常。,则表示不得抛出任何异常。异常规范(异常规范(exception specification)提供了一种方案,可以)提供了一种方案,可以随着函数声明列出该函数可能抛出的异常,并保证该函数不会随着函数声明列出该函数可能抛出的异常,并保证该函数不会抛出任何其他类型的异常,在抛出任何其他类型的异常,在stack类定义中可有:类定义中可有: void Push(const T&data) throw(pushOnFull) ;T Pop() throw(popOnEmpty); 成员函数类内声明和类外
51、定义必须成员函数类内声明和类外定义必须必须在两处都有相同的异常必须在两处都有相同的异常规范规范,同样的异常规范。同样的异常规范。异常规范的引入:异常规范的引入:鹿华塑要妄勇谁绽诚膝丛钢撼窃肘棺潘凉换吵吭底跺诊丘郝嘉忽卞盐笨单c+基础知识10异常处理机制c+基础知识10异常处理机制10.6 异常规范异常规范class CBasepublic: virtual int fun1(int) throw(); virtual int fun2(int) throw(int); virtual string fun3() throw(int,string);class CDerived:public C
52、Basepublic: int fun1(int) throw(int); /错!异常规范不如错!异常规范不如throw()严格严格 int fun2(int) throw(int); /对!有相同的异常规范对!有相同的异常规范 string fun3() throw(string); /对!异常规范比对!异常规范比 throw(int,string)更严格更严格 虚函数中的异常规范:虚函数中的异常规范:派生类的虚拟函数的异常规范必须与基类虚函数的异常一样或派生类的虚拟函数的异常规范必须与基类虚函数的异常一样或更严格。因为当派生类的虚函数被指向基类类型的指针调更严格。因为当派生类的虚函数被指向
53、基类类型的指针调用时,用时,保证不会违背基类成员函数的异常规范保证不会违背基类成员函数的异常规范。 脾翼浮逐寡肋缚暑奔痊享拆书荤藉值旋镶犀挥强厘殃假储型溢予症凳檀男c+基础知识10异常处理机制c+基础知识10异常处理机制10.7 C+标准库的异常类层次结构标准库的异常类层次结构(选读选读)exception类的接口如下:类的接口如下:namespace std /注意在名字空间域注意在名字空间域std中中 class exception public: exception() throw() ; /默认构造函数默认构造函数 exception(const exception &) throw(
54、) ; /复制构造函数复制构造函数 exception &operator=(const exception&) throw() ; /复制赋值操作符复制赋值操作符 virtual exception() throw() ; /析构函数析构函数 virtual const char* what() const throw() ; /返回一个返回一个C风格的字符串,风格的字符串,目的是为抛出的异常提供文本描述目的是为抛出的异常提供文本描述 ;C+标准库中的异常层次的根类被称为标准库中的异常层次的根类被称为exception,定义在库的头文件定义在库的头文件中中懦曳劈摈爪盘靠棒骡删韵晶尝完嘴衙婿奋
55、炸帛尊渺肥拒追注遣钎谊堆垢债c+基础知识10异常处理机制c+基础知识10异常处理机制10.7 C+标准库的异常类层次结构标准库的异常类层次结构(选读选读)C+C+标准库提供的逻辑异常:标准库提供的逻辑异常:invalid_argument异常,接收到一个无效的实参,抛出该异常。异常,接收到一个无效的实参,抛出该异常。out_of_range异常,收到一个不在预期范围中的实参,则抛出。异常,收到一个不在预期范围中的实参,则抛出。length_error异常,报告企图产生异常,报告企图产生“长度值超出最大允许值长度值超出最大允许值”的对象的对象domain_error异常,用以报告域错误(异常,用
56、以报告域错误(domain error)。)。C+C+标准库提供的运行时异常:标准库提供的运行时异常:range_error异常,报告内部计算中的范围错误。异常,报告内部计算中的范围错误。overflow_error异常,报告算术溢出错误。异常,报告算术溢出错误。underflow_error异常,报告算术下溢错误。异常,报告算术下溢错误。以上三个异常是由以上三个异常是由runtime_error类派生的。类派生的。bad_alloc异常。当异常。当new()操作符不能分配所要求的存储区时,会抛操作符不能分配所要求的存储区时,会抛出该异常。它是由基类出该异常。它是由基类exception派生的
57、。派生的。【例【例10.3】为类模板为类模板Array重新定义下标操作符重新定义下标操作符,如果索,如果索引值越界,它会抛出一个引值越界,它会抛出一个out_of_range类型的异常。类型的异常。荣航丫遣渍宦喇肇娩使咀序竭吕藉嘉勃农坦翅蚊幽茹盅澳苯褒豫掀植腋夜c+基础知识10异常处理机制c+基础知识10异常处理机制第十章第十章 异常处理异常处理完完谢谢!谢谢!混痪瑞叶升摸松观塌贱恕漏废豢货蜒能稠垒员捻剩螟久读悯怎萍酱惮圃真c+基础知识10异常处理机制c+基础知识10异常处理机制 例例10.1 10.1 堆栈异常堆栈异常处理处理templateclass pushOnFull /栈满异常声明栈
58、满异常声明T _value;public:pushOnFull(T i)_value=i;T value()return _value;void print()cerr栈满,栈满,value()未压入栈未压入栈endl;templateclass popOnEmpty/栈空异常声明栈空异常声明public:void print()cerr栈已空,无法出栈栈已空,无法出栈endl;谍忙缴弗悲麦绞裙调氟励勤察玫年塞喻酉良变瞪议骡拄润踪君酱框耙正殴c+基础知识10异常处理机制c+基础知识10异常处理机制 例例10.1 10.1 堆栈异常堆栈异常处理处理templateclass Stackint t
59、op; /栈顶指针(下标)栈顶指针(下标)T *elements; /动态建立的数值动态建立的数值int maxSize; /栈最大允纳的元素个数栈最大允纳的元素个数public:Stack(int=20); /栈如不指定大小,设为栈如不指定大小,设为20元素元素Stack()delete elements;void Push(const T &data) throw(pushOnFull); /压栈压栈T Pop() throw(popOnEmpty); /弹出,弹出,topT GetElem(int i)return elementsi; /返回指定元素返回指定元素void MakeEmp
60、ty()top= 1; /清空栈清空栈bool IsEmpty() constreturn top= 1; /判栈空判栈空bool IsFull() constreturn top=maxSize1; /判栈满判栈满void PrintStack(); /输出栈内所有数据输出栈内所有数据;畔着偏酗铀悬岔睹沿溶旋囚青匙恍席淤果淘轿伯析仿地誊寺觉抡营恤暗拍c+基础知识10异常处理机制c+基础知识10异常处理机制 例例10.1 10.1 堆栈异常堆栈异常处理处理template void Stack:Push(const T &data)if(IsFull() throw pushOnFull(da
61、ta); /栈满则抛出异常栈满则抛出异常elements+top=data; /栈顶指针先加栈顶指针先加1,元素再进栈,元素再进栈,top是指向栈顶元素是指向栈顶元素templateT Stack:Pop() if(IsEmpty() throw popOnEmpty(); /栈已空则不能退栈,抛出异常栈已空则不能退栈,抛出异常return elementstop;/返回栈顶元素,同时栈顶指针退返回栈顶元素,同时栈顶指针退1溯惩瞥式含妻贯谭判愚腥晓滇操淌吗悄褂贿靶帜掣届酱孔拘紧乒药紧掸穷c+基础知识10异常处理机制c+基础知识10异常处理机制 例例10.1 10.1 堆栈异常堆栈异常处理处理i
62、nt main()int a9=1,2,3,4,5,6,7,8,9, b9=0,i;Stackistack(8);tryfor(i=0;i9;i+) istack.Push(ai); /到到a8时栈满时栈满,异常异常istack.PrintStack();catch(pushOnFull&eObj)eObj.print();tryfor(i=0;i9;i+) bi=istack.Pop();catch(popOnEmpty&eObj) eObj.print();for(i=0;i9;i+) coutbit;coutendl;return 0;输拆卤煮媚耸接世攫府雏你悬什浆觉虾芹屯蔚赏胞斧湛窿偶
63、沂歪噶蔽拈拍c+基础知识10异常处理机制c+基础知识10异常处理机制 例例10.1_110.1_1函数函数trytry块(块(Function Function try Blocktry Block)int main()tryint a9=1,2,3,4,5,6,7,8,9, b9=0,i;Stack istack(8);for(i=0;i9;i+) istack.Push(ai); /到到a8时栈满时栈满,异常异常istack.PrintStack();for(i=0;i9;i+) bi=istack.Pop();for(i=0;i9;i+) coutbit; /发生异常后不会执行发生异常后
64、不会执行coutendl;return 0;catch(pushOnFull & eObj)eObj.print();return 1;catch(popOnEmpty & eObj)eObj.print();return 2;狂菏惊豫敷挥帅墅傅梨蛔殷票乡椒柞遍诵麓潜搪痪坊胃舰恋镶离茁也集贴c+基础知识10异常处理机制c+基础知识10异常处理机制 例例10.2 10.2 异常层次结构中的虚函异常层次结构中的虚函数数class Excppublic: virtual void print()cerr发生异常发生异常endl;class stackExcp:public Excppublic: v
65、irtual void print()cerr栈发生异常栈发生异常endl;class pushOnFull:public stackExcppublic: virtual void print()cerr栈满栈满,不能压栈不能压栈endl;class popOnEmpty:public stackExcppublic: void print()cerr栈已空,无法出栈栈已空,无法出栈endl;瓣骚栅求楚内钟匿绷卫迁甭捧榔禄恬哲杖埋科擂敷宅瓦丫涟按嚣图扭炔另c+基础知识10异常处理机制c+基础知识10异常处理机制 例例10.2 10.2 异常层次结构中的虚函异常层次结构中的虚函数数int ma
66、in()int a9=1,2,3,4,5,6,7,8,9, b9=0,i;Stack istack(8);tryfor(i=0;i9;i+) istack.Push(ai); /到到a8时栈满时栈满,异常异常istack.PrintStack();catch(Excp&eObj)eObj.print(); /调用虚函数调用虚函数pushOnFull:print()tryfor(i=0;i9;i+) bi=istack.Pop();catch(Excp&eObj)eObj.print(); /调用虚函数调用虚函数popOnEmpty:print()for(i=0;i9;i+) coutbit;c
67、outendl;return 0;catch子句输出为:子句输出为:栈满,不能压栈栈满,不能压栈栈已空,无法出栈栈已空,无法出栈清滔瓢足暑迟匠隆摘坏迈蛆礼挛窒近控灿嫡走惮胳该磊盟删陷滇甸充滔喜c+基础知识10异常处理机制c+基础知识10异常处理机制例例10.3 Array重新定义下标操作符重新定义下标操作符 templateclass Arrayint size;elemType * ia ;public:explicit Array(int sz=DefaultArraySize)size=sz;ia=new elemType size; Array()delete ia;elemType
68、& operator(int ix) const /下标运算符下标运算符 重载重载if(ix=size) /增加异常抛出增加异常抛出,防止索引值越界防止索引值越界string eObj=out_of_range error in Array:operator();throw out_of_range(eObj);return iaix; /保留原来保留原来 的所有索引方式的所有索引方式private:int size;elemType * ia;毛哥调遗簇陈穗胎吗淄遥风旅烂没败迹极掇轮歉赋脐卓堡厚募教仗魂男灶c+基础知识10异常处理机制c+基础知识10异常处理机制例例10.3 Array重新定
69、义下标操作符重新定义下标操作符 int main()int i;Array arr;tryfor(i=0;i=DefaultArraySize;i+)arri=i+1; /写入写入ia10时出界时出界coutsetw(5)arri; coutendl; catch(const out_of_range & excp)cerrnexcp.what()n;/打印打印out_of_range error in Array:operator()return 1; return 0; 晦司签翰置争影寥剁骗言彦摇税搅未鼓呛寥茬卜篙吼沧耀殷燃宙喧伦扩亿c+基础知识10异常处理机制c+基础知识10异常处理机制