c++-unit10异常处理市公开课一等奖省赛课微课金奖课件_第1页
c++-unit10异常处理市公开课一等奖省赛课微课金奖课件_第2页
c++-unit10异常处理市公开课一等奖省赛课微课金奖课件_第3页
c++-unit10异常处理市公开课一等奖省赛课微课金奖课件_第4页
c++-unit10异常处理市公开课一等奖省赛课微课金奖课件_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第十章异常处理

大型和十分复杂程序往往会产生一些极难查找甚至是无法防止运行时错误。当发生运行时错误时,不能简单地结束程序运行,而是退回到任务起点,指犯错误,并由用户决定下一步工作。面向对象异常处理(exceptionhandling)机制是C++语言用以处理这个问题有力工具。

函数执行时,放在try(测试)程序块中任何类型数据对象发生异常,都可被throw表示式抛出,随即逆调用链退回,直到被catch子句捕捉,并在此执行异常处理,汇报出现异常等情况。从抛出到捕捉,应将各嵌套调用函数残余在栈中自动对象、自动变量和现场保护内容等进行去除。假如已退到入口函数还未捕捉则由terminate()函数来终止入口函数。1/40第十章异常处理10.1异常概念

10.3栈展开与异常捕捉

10.2异常处理机制

10.5异常和继承

10.7C++标准库异常类层次结构

(选读)10.6异常规范(选读)10.4异常重新抛出和catch_all子句

2/4010.1

异常概念

异常概念引入:

异常(exception)是程序可能检测到,运行时不正常情况,如存放空间耗尽、数组越界、被0除等等。能够预见可能发生在什么地方,不过无法确知怎样发生和何时发生。尤其在一个大型程序(软件)中,程序各部分是由不一样小组编写,它们由公共接口连起来,错误可能就发生在相互配合上,也可能发生在事先根本想不到个别条件组合上。C++提供了一些内置语言特征来产生(raise)或抛出(throw)异常,用以通知“异常已经发生”,然后由预先安排程序段来捕捉(catch)异常,并对它进行处理。这种机制能够在C++程序两个无关(往往是独立开发)部分进行“异常”通信。由程序某一部分引发了另一部分异常,这一异常可回到引发异常部分去处理(逆着程序函数调用链)。3/4010.2

异常处理机制测到栈满或空就抛出一个异常。template<typenameT>voidStack<T>::Push(constT&data){

if(IsFull())throwpushOnFull<T>(data);

//注意加了括号,是结构一个无名对象

elements[++top]=data;}template<typenameT>TStack<T>::Pop(){

if(IsEmpty())throwpopOnEmpty<T>();

returnelements[top--];}注意pushOnFull是类,C++要求抛出必须是对象,所以必须有“()”,即调用结构函数建立一个对象。异常与异常抛出:以栈为例,异常类申明以下:classpopOnEmpty{...};//栈空异常classpushOnFull{...};//栈满异常4/4010.2

异常处理机制throw表示式抛出异常为异常处理第一步。在堆栈压栈和出栈操作中发生错误而抛出异常,理所当然地应由调用堆栈程序来处理。异常并非总是类对象,throw表示式也能够抛出任何类型对象,如枚举、整数等等。但最惯用是类对象。在C++中异常抛出与异常处理之间有一整套程序设计机制。首先采取关键字try,组成一个try块(tryblock),它包含了抛出异常语句。当然也能够是包含了这么调用语句,该语句所调用函数中有能够抛出异常语句。异常处理机制:5/4010.2

异常处理机制intmain(){

inta[9]={1,2,3,4,5,6,7,8,9},b[9]={0},i;stack<int>istack(8);

try{for(i=0;i<9;i++)istack.Push(a[i]);istack.PrintStack();}

catch(pushOnFull<int>){cerr<<”栈满”<<endl;}

try{for(i=0;i<9;i++){b[i]=istack.Pop();}}

catch(popOnEmpty<int>){cerr<<”栈空”<<endl;}

for(i=0;i<9;i++)cout<<b[i]<<’\t’;cout<<endl;

return0;}try块与catch子句关系实例:6/4010.2

异常处理机制由catch字句捕捉并处理异常是第二步。注意与catch语句分别匹配是在压栈和出栈组员函数模板中throw语句,一个抛出pushOnFull类无名对象,另一个抛出popOnEmpty类无名对象。在编制程序时有一条通例:把正常执行程序与异常处理两部分分隔开来,这么使代码更易于跟随和维护。在上例中,我们能够把两个try块合成一个,而把两个catch子句都放在函数最终。

说明:这里有两个try块,分别对应压栈与出栈;也有两个catch子句(catchclause),分别处理压栈时栈满和出栈时栈空。

7/4010.2

异常处理机制1.假如没有异常发生,继续执行try块中代码,与try块相关联catch子句被忽略,程序正常执行,main()返回0。2.当第一个try块在for循环中抛出异常,则该for循环退出,try块也退出,去执行可处理pushOnFull异常catch子句。istack.PrintStack()不再执行,被忽略。3.假如第二个try块调用Pop()抛出异常,则退出for和try块,去执行可处理popOnEmpty异常catch子句。4.当某条语句抛出异常时,跟在该语句后面语句将被跳过。程序执行权交给处理异常catch子句,假如没有catch子句能够处理异常,则交给C++标准库中定义terminate()。流程控制规则:8/4010.3栈展开与异常捕捉

catch子句由三部分组成:关键字catch、圆括号中异常申明以及复合语句中一组语句。catch子句不是函数,所以圆括号中不是形参,而是一个异常类型申明,能够是类型也能够是对象。catch子句使用:它只有一个子句,没有定义和调用之分。使用时由系统按规则自动在catch子句列表中匹配。

catch子句能够包含返回语句(return),也可不包含返回语句。包含返回语句,则整个程序结束。而不包含返回语句,则执行catch列表之后下一条语句。

catch子句说明:当try块中语句抛出异常时,系统经过查看跟在其后catch子句列表,来查找可处理该异常catch子句。9/4010.3栈展开与异常捕捉对应在throw表示式中,结构抛出对象也要有实参:throwpushOnFull(data);

//data即Push(const&data)中参数datatemplate<typenameT>classpushOnFull{T_value;public:pushOnFull(Ti):_value(i){}

//或写为pushOnFull(Ti){_value=i;}Tvalue(){return

_value;}};新私有数据组员_value保留那些不能被压入栈中值。该值即调用结构函数时实参。catch子句异常申明探讨:异常申明中能够是一个对象申明。以栈为例。当栈满时,要求在异常对象中保留不能被压入到栈中值,pushOnFull类可定义以下:10/4010.3栈展开与异常捕捉

在catch子句中,要取得_value,须调用pushOnFull

中组员函数value():catch(pushOnFull<T>eObj){cerr<<”栈满”<<eObj.value()<<”未压入栈”<<endl;

return1;}在catch子句异常申明中申明了对象eObj,用它来调用pushOnFull类对象组员函数value()。异常对象是在抛出点被创建,与catch子句是否显式要求创建一个异常对象无关,该对象总是存在,在catch子句中只是为了调用异常处理对象组员函数才申明为对象,不用类。*catch子句异常申明中采取对象只是一个形式。甚至异常并非一个类对象时,也能够用一样格式,比如异常为一枚举量,这时就等效于按值传递,而不是调用类对象公有组员。11/4010.3栈展开与异常捕捉catch子句异常申明与函数参数申明类似,能够是按值传送,也能够是按引用传递。对大型类对象降低无须要复制是很有意义,所以对于类类型异常,其异常申明最好也是被申明为引用。如:catch(pushOnFull<T>&eObj){cerr<<”栈满”<<eObj.value()<<”未压栈”<<endl;

return1;}使用引用类型异常申明,catch子句能够修改异常对象,但仅仅是异常对象本身,正常程序部分量并不会被修改。与普通类对象不一样,实际上异常对象处理完后,生命期也就结束了。只有需要重新抛出异常(在下一节中讨论),修改操作才有意义。

【例10.1】包含栈满或空异常完整程序。12/4010.3栈展开与异常捕捉把程序正常处理代码和异常处理代码分离最清楚方法是定义函数try块(FunctiontryBlock)。这种方法是把整个函数包含在try块中。

一个函数try块把一组catch子句同一个函数体相关联。假如函数体中语句抛出一个异常,则考虑跟在函数体后面处理代码来处理该异常。函数try块对结构函数尤其有用。【例10.1_1】定义函数try块(FunctiontryBlock)。

函数try块使用:13/40寻找匹配catch子句有固定过程:假如throw表示式位于try块中,则检验与try块相关联catch子句列表,看是否有一个子句能够处理该异常,有匹配,则该异常被处理;找不到匹配catch子句,则在主调函数中继续查找。假如一个函数调用在退出时带有一个被抛出异常未能处理,而且这个调用位于一个try块中,则检验与该try块相关联catch子句列表,看是否有一个子句匹配,有,则处理该异常;没有,则查找过程在该函数主调函数中继续进行。即这个查找过程逆着嵌套函数调用链向上继续,直到找处处理该异常catch子句。只要碰到第一个匹配catch子句,就会进入该catch子句,进行处理,查找过程结束。

14/4010.3栈展开与异常捕捉

在栈异常处理例子中,对popOnEmpty,首先应在istack组员函数Pop()中找,因为Pop()中没有try块,不存在catch子句,所以Pop()带着一个异常退出。下一步是检验调用Pop()函数,这里是main(),在main()中对Pop()调用位于一个try块中,则可用与该try块关联catch子句列表中某一个来处理,找到第一个popOnEmpty类型异常申明catch子句,并进入该子句进行异常处理。

栈展开:因发生异常而逐步退出复合语句和函数定义过程,被称为栈展开(stackunwinding)。这是异常处理关键技术。异常对程序影响通常不但是在发生异常那个局部范围中,而且可能逆调用链而上,甚至整个任务。所以,异常处理应该在其对程序影响终止处进行,甚至是在调用该任务菜单处进行。15/4010.3栈展开与异常捕捉

在栈展开期间,在退出域中有某个局部量是类对象,栈展开过程将自动调用该对象析构函数,完成资源释放。所以C++异常处理过程本质上反应是“资源获取是由结构函数实现,而资源释放是由析构函数完成”。采取面向对象程序设计,取得资源动作封装在类结构函数中,释放资源动作封装在类析构函数中,当一个函数带着未处理异常退出时,函数中这种类对象被自动销毁,资源(包含动态空间分配资源和打开文件)释放。所以由文件重构对象应该放在结构函数中,把对象存入文件应该放在析构函数中。栈展开时资源释放:异常处理应该用于面向对象程序设计。对非面向对象程序设计假如函数动态取得过资源,因异常,这些资源释放语句可能被忽略,则这些资源将永远不会被自动释放。16/4010.3栈展开与异常捕捉

异常不能够保持在未被处理状态。异常表示一个程序不能够继续正常执行,这是非常严重问题,假如没有找处处理代码,程序就调用C++标准库中定义函数terminate()。异常对象探讨:异常对象是在throw表示式中建立并抛出:throw表示式经过调用异常类结构函数创建一个暂时对象,然后把这个暂时对象复制到一个被称为异常对象(exceptionobject)存贮区中,它确保会连续到异常被处理完。

17/4010.3栈展开与异常捕捉函数调用和异常处理区分:建立函数调用所需要全部信息在编译时已经取得,而异常处理机制要求运行时支持。对于普通函数调用,经过函数重载解析过程,编译器知道在调用点上哪个函数会真正被调用。但对于异常处理,编译器不知道特定throw表示式catch子句在哪个函数中,以及在处理异常之后执行权被转移到哪儿。这些都在运行时刻决定,异常是随机发生,异常处理catch子句是逆调用链进行查找,这与运行时多态­——虚函数也是不一样。当一个异常不存在处理代码时,系统无法通知用户,所以要有terminate()函数,它是一个运行机制,当没有处理代码(catch子句)能够匹配,被抛出异常时由它通知用户。18/4010.4异常重新抛出和catch_all子句(选读)

rethrow表示式仍为:throw;

但仅有一个关键字,因为异常类型在catch语句中已经有了,无须再指明。被重新抛出异常就是原来异常对象。不过重新抛出异常catch子句应该把自己做过工作告诉下一个处理异常catch子句,往往要对异常对象做一定修改,以表示一些信息,所以catch子句中异常申明必须被申明为引用,这么修改才能真正做在异常对象本身中,而不是副本中。

异常重新抛出与连续处理:当catch语句捕捉一个异常后,可能不能完全处理异常,完成一些操作后,该异常必须由函数链中更上级函数来处理,这时catch子句能够重新抛出(rethrow)该异常,把异常传递给函数调用链中更上级另一个catch子句,由它进行深入处理。19/4010.4异常重新抛出和catch_all子句(选读)通用catch子句(catch_all):catch(...){代码*/}

任何异常都能够进入这个catch子句。这里三个点称为省略号。花括号中复合语句用来执行指定操作。异常发生后按栈展开(stackunwinding)退出,动态分配非类对象资源不会自动释放,通常在catch_all子句中释放。voidfun1(){

int*res;res=newint[100];//定义一个资源对象

try{//代码包含使用资源res和一些可能引发异常抛出操作

}//异常可能有各种

catch(...){//不论是那种异常都在此释放

delete[]res;//释放资源对象res

throw;}//重新抛出异常

delete[]res;}//正常退出前释放资源对象res;20/4010.4异常重新抛出和catch_all子句(选读)

catch_all子句能够单独使用,也能够与其它catch子句联合使用。假如联合使用,它必须放在相关catch子句表最终。

catch子句被检验次序与它们在try块之后排列次序相同,一旦找到了一个匹配,则后续catch子句将不再检验,按此规则,catch_all子句(catch(...){})处理表前面所列各种异常之外异常。假如只用catch_all子句进行某项操作,则其它操作应由catch子句重新抛出异常,逆调用链去查找新处理子句来处理,不能在子句列表中再安排一个处理同一异常子句,因为第二个子句是永远执行不到。通用catch子句应用:21/4010.5异常和继承定义一个称为Excp基类,由它来打印错误信息:classExcp{public:voidprint(stringmsg){cerr<<msg<<endl;}};再从该基类派生出两个异常类:classstackExcp:publicExcp{...};//栈异常类基类classmathExcp:publicExcp{...};//数学库异常基类深入派生出其它异常类:classpopOnEmpty:publicstackExcp{...};//栈空退栈异常classpushOnFull:publicstackExcp{...};//栈满压栈异常classzeroOp:publicmathExcp{...};//数学库零操作异常classdivideByZero:publicmathExcp{...};//数学库被零除异常异常层次结构:在C++程序中,表示异常类通常被组成为一个组(即如在前面各节讨论那样)或者一个层次结构。形成了三层结构。22/4010.5异常和继承在层次结构下,异常抛出会有一些不一样,以下做法是错:if(full()){pushOnFullexcept(data);stackExcp*pse=&except;//pse指向类对象为pushOnFull

throw*pse;}//抛出异常对象类型为stackExcp 这里被创建异常类对象是stackExcp类类型,尽管pse指向一个实际类型为pushOnFull对象,但那是一个暂时对象,复制到异常对象存放区中时创建却是stackExcp类异常对象。所以该异常不能被pushOnFull类型catch子句处理。层次结构异常抛出:23/4010.5异常和继承在处理类类型异常时,catch子句排列次序是非常主要。catch(pushOnFull){...}//处理pushOnFull异常catch(stackExcp){...}//处理栈其它异常catch(Excp){...}//处理普通异常派生类类型catch子句必须先出现,以确保只有在没有其它catch子句适用时,才会进入基类类型catch子句。异常catch子句无须是与异常最匹配catch子句,而是最先匹配到catch子句,就是第一个碰到能够处理该异常catch子句。所以在catch子句列表中最特化(匹配条件最严格)catch子句必须先出现。catch子句排列次序:24/4010.5异常和继承类层次结构异常一样能够重新抛出(rethrow),把一个异常传递给函数调用列表中更上层另一个catch子句:throw;类层次结构下异常重新抛出:重新抛出异常仍是原来异常对象。假如程序中抛出了pushOnFull类类型异常,而它被基类catch子句处理,并在其中再次被抛出,那么这个异常仍是pushOnFull类异常,而不是其基类异常。25/4010.5异常和继承在基类catch子句处理是异常对象基类子对象一份副本,该副本只在该catch子句中被访问,重新抛出是原来异常对象。这个放在异常对象存放区中异常生命期应该是在处理该异常一系列子句中最终一个退出时才结束,也就是直到这时,才由异常类析构函数来销毁它。这一系列子句是由重新抛出联络起来。【例10.2】异常层次结构中虚函数。为了调用派生类对象虚拟函数,异常申明必须为一个指针或引用。

虚函数是类层次结构中多态性基本伎俩,异常类层次结构中也能够定义虚拟函数。

26/4010.6

异常规范一个函数异常规范违例只能在运行时才能被检测出来。假如在运行时,函数抛出了一个没有被列在它异常规范中异常时(而且函数中所抛出异常,没有在该函数内部处理)则系统调用C++标准库中定义函数unexpected()。仅当函数中所抛出异常,没有在该函数内部处理,而是逆调用链回溯寻找匹配catch子句时候,异常规范才起作用。假如异常规范为throw(),则表示不得抛出任何异常。异常规范(exceptionspecification)提供了一个方案,能够伴随函数申明列出该函数可能抛出异常,并确保该函数不会抛出任何其它类型异常,在stack类定义中可有:

voidPush(constT&data)throw(pushOnFull);TPop()throw(popOnEmpty);组员函数类内申明和类外定义必须必须在两处都有相同异常规范,一样异常规范。异常规范引入:27/4010.6

异常规范classCBase{public:

virtualintfun1(int)throw();

virtualintfun2(int)throw(int);

virtualstringfun3()throw(int,string);};classCDerived:publicCBase{public:

intfun1(int)throw(int);

//错!异常规范不如throw()严格

intfun2(int)throw(int);//对!有相同异常规范

stringfun3()throw(string);}//对!异常规范比throw(int,string)更严格

虚函数中异常规范:派生类虚拟函数异常规范必须与基类虚函数异常一样或更严格。因为当派生类虚函数被指向基类类型指针调用时,确保不会违反基类组员函数异常规范。

28/4010.7C++标准库异常类层次结构(选读)exception类接口以下:namespacestd{//注意在名字空间域std中

classexception{public:exception()throw();//默认结构函数

exception(constexception&)throw();//复制结构函数

exception&operator=(constexception&)throw();

//复制赋值操作符

virtual~exception()throw();//析构函数

virtual

constchar*what()

const

throw();

//返回一个C格调字符串,目标是为抛出异常提供文本描述

};}C++标准库中异常层次根类被称为exception,定义在库头文件<exception>中29/4010.7C++标准库异常类层次结构(选读)C++标准库提供逻辑异常:invalid_argument异常,接收到一个无效实参,抛出该异常。out_of_range异常,收到一个不在预期范围中实参,则抛出。length_error异常,汇报企图产生“长度值超出最大允许值”对象domain_error异常,用以汇报域错误(domainerror)。C++标准库提供运行时异常:range_error异常,汇报内部计算中范围错误。overflow_error异常,汇报算术溢犯错误。underflow_error异常,汇报算术下溢错误。以上三个异常是由runtime_error类派生。bad_alloc异常。当new()操作符不能分配所要求存放区时,会抛出该异常。它是由基类exception派生。【例10.3】为类模板Array重新定义下标操作符[],假如索引值越界,它会抛出一个out_of_range类型异常。30/40第十章异常处理完谢谢!31/40[例10.1]堆栈异常处理template<typenameT>classpushOnFull{//栈满异常申明

T_value;public: pushOnFull(Ti){_value=i;} Tvalue(){return_value;}

voidprint(){cerr<<"栈满,"<<value()<<"未压入栈"<<endl;}};template<typenameT>classpopOnEmpty{//栈空异常申明public:

voidprint(){cerr<<"栈已空,无法出栈"<<endl;}};32/40[例10.1]堆栈异常处理template<typenameT>classStack{

inttop;//栈顶指针(下标)

T*elements;//动态建立数值

intmaxSize;//栈最大允纳元素个数public: Stack(int=20);//栈如不指定大小,设为20元素

~Stack(){delete[]elements;}

voidPush(constT&data)throw(pushOnFull<T>);//压栈

TPop()throw(popOnEmpty);//弹出,top-- TGetElem(inti){returnelements[i];}//返回指定元素

voidMakeEmpty(){top=-1;}//清空栈

boolIsEmpty()const{returntop==-1;}//判栈空

boolIsFull()const{returntop==maxSize-1;}//判栈满

voidPrintStack();//输出栈内全部数据};33/40[例10.1]堆栈异常处理template<typenameT>voidStack<T>::Push(constT&data){

if(IsFull())throwpushOnFull<T>(data);//栈满则抛出异常

elements[++top]=data;

//栈顶指针先加1,元素再进栈,top是指向栈顶元素}template<typenameT>TStack<T>::Pop(){

if(IsEmpty())throwpopOnEmpty<T>();

//栈已空则不能退栈,抛出异常

returnelements[top--]; //返回栈顶元素,同时栈顶指针退1}34/40[例10.1]堆栈异常处理intmain(){

inta[9]={1,2,3,4,5,6,7,8,9},b[9]={0},i; Stack<int>istack(8);

try{

for(i=0;i<9;i++)istack.Push(a[i]);//到a[8]时栈满,异常

istack.PrintStack(); }

catch(pushOnFull<int>&eObj){eObj.print();}

try{for(i=0;i<9;i++)b[i]=istack.Pop();}

catch(popOnEmpty<int>&eObj){eObj.print();}

for(i=0;i<9;i++)cout<<b[i]<<'\t'; cout<<endl;

return0;}35/40[例10.1_1]函数try块(FunctiontryBlock)intmain()try{

inta[9]={1,2,3,4,5,6,7,8,9},b[9]={0},i; Stack<int>istack(8);

for(i=0;i<9;i++)istack.Push(a[i]);//到a[8]时栈满,异常

istack.PrintStack();

for(i=0;i<9;i++)b[i]=istack.Pop();

for(i=0;i<9;i++)cout<<b[i]<<'\t';//发生异常后不会执行

cout<<endl;

return0;}catch(pushOnFull<int>&eObj){ eObj.print();return1;}catch(popOnEmpty<int>&eObj){ eObj.print();return2;}36/40[例10.2]异常层次结构中虚函数classExcp{public:

virtualvoidprint(){cerr<<"发生异常"<<endl;}};classstackExcp:publicExcp{public:

virtualvoidprint(){cerr<<"栈发生异常"<<endl;}};classpushOnFull:publicstackExcp{public:

virtualvoidprint(){cerr<<"栈满,不能压栈"<<en

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论