●。。第十二章异常处理 大型和十分复杂的程序往往会产生一些很难查找的甚至是 无法避免的运行时错误。当发生运行时错误时,不能简单地结 束程序运行,而是退回到任务的起点,指出错误,并由用户决 定下一步工作。面向对象的异常处理( exception handling) 机制是C++语言用以解决这个问题的有力工具。 程序的错误有两种,一种是编译错误,即语法错误。如果 使用了错误的语法、函数、结构和类,程序就无法被生成运行 代码。另一种是在运行时发生的错误,它分为不可预料的逻辑 错误和可以预料的运行异常,这里所讲的异常( exception)是 程序可能检测到的,运行时不正常的情况,如存储空间耗尽 数组越界、被0除等等,可以预见可能发生在什么地方,但是无 法确知怎样发生和何时发生。特别在一个大型的程序(软件) 中,程序各部分是由不同的小组编写的,它们由公共接口连起 来,错误可能就发生在相互的配合上,也可能发生在事先根本 想不到的个别的条件组合上
第十二章 异常处理 大型和十分复杂的程序往往会产生一些很难查找的甚至是 无法避免的运行时错误。当发生运行时错误时,不能简单地结 束程序运行,而是退回到任务的起点,指出错误,并由用户决 定下一步工作。面向对象的异常处理(exception handling) 机制是C++语言用以解决这个问题的有力工具。 程序的错误有两种,一种是编译错误,即语法错误。如果 使用了错误的语法、函数、结构和类,程序就无法被生成运行 代码。另一种是在运行时发生的错误,它分为不可预料的逻辑 错误和可以预料的运行异常,这里所讲的异常(exception)是 程序可能检测到的,运行时不正常的情况,如存储空间耗尽、 数组越界、被0除等等,可以预见可能发生在什么地方,但是无 法确知怎样发生和何时发生。特别在一个大型的程序(软件) 中,程序各部分是由不同的小组编写的,它们由公共接口连起 来,错误可能就发生在相互的配合上,也可能发生在事先根本 想不到的个别的条件组合上
● 第十二章异常处理 为处理可预料的错误,常用的典型方法是让被调用函数返 回某一个特别的值(或将某个按引用调用传递的参数设置为 个特别的值),而外层的调用程序则检查这个错误标志,从而 确定是否产生了某一类型的错误。另一种典型方法是当错误发 生时跳出当前的函数体,控制转向某个专门的错误处理程序, 从而中断正常的控制流。这两种方法都是权宜之计,不能形成 强有力的结构化异常处理模式。 异常处理机制是用于管理程序运行期间错误的一种结构化 方法。所谓结构化是指程序的控制不会由于产生异常而随意跳 转。异常处理机制将程序中的正常处理代码与异常处理代码显 式区别开来,提高了程序的可读性 异常处理在C+编程中已经普遍采用,成为提高程序健壮 性的重要手段之一
第十二章 异常处理 为处理可预料的错误,常用的典型方法是让被调用函数返 回某一个特别的值(或将某个按引用调用传递的参数设置为一 个特别的值),而外层的调用程序则检查这个错误标志,从而 确定是否产生了某一类型的错误。另一种典型方法是当错误发 生时跳出当前的函数体,控制转向某个专门的错误处理程序, 从而中断正常的控制流。这两种方法都是权宜之计,不能形成 强有力的结构化异常处理模式。 异常处理机制是用于管理程序运行期间错误的一种结构化 方法。所谓结构化是指程序的控制不会由于产生异常而随意跳 转。异常处理机制将程序中的正常处理代码与异常处理代码显 式区别开来,提高了程序的可读性。 异常处理在C++编程中已经普遍采用,成为提高程序健壮 性的重要手段之一
12.1异常处的机制國回 C++语言异常处理机制的基本思想是将异常的检测与处 理分离。当在一个函数体中检测到异常条件存在,但无法确定 相应的处理方法时,将引发一个异常,并由函数的直接或间接 调用检测并处理这个异常。这一基本思想用3个保留字实现: throw、try和 catch。其作用是: (1)try:标识程序中异常语句块的开始。 (2) throy:用来创建用户自定义类型的异常错误。 (3) catch:标识异常错误处理模块的开始。 在C++程序中,任何需要检测异常的语句(包括函数调 用)都必须在try语句块中执行,异常必须由紧跟着try语句后 面的 catch语句来捕获并处理。因而,try与 catch总是结合使 用。thow、try和 catch语句的一般语法如下:
12.1 异常处理的机制 C++语言异常处理机制的基本思想是将异常的检测与处 理分离。当在一个函数体中检测到异常条件存在,但无法确定 相应的处理方法时,将引发一个异常,并由函数的直接或间接 调用检测并处理这个异常。这一基本思想用3个保留字实现: throw、try和catch。其作用是: (1)try:标识程序中异常语句块的开始。 (2)throw:用来创建用户自定义类型的异常错误。 (3)catch:标识异常错误处理模块的开始。 在C++程序中,任何需要检测异常的语句(包括函数调 用)都必须在try语句块中执行,异常必须由紧跟着try语句后 面的catch语句来捕获并处理。因而,try与catch总是结合使 用。throw、try和catch语句的一般语法如下:
●。。12.,1异常处理的机制 throw; /try语句块 catch(类型1参数1) //针对类型1的异常处理 catch(类型2参数2) //针对类型2的异常处理 catch(类型n参数n) /针对类型n的异常处理
12.1 异常处理的机制 throw ; try { //try语句块 } catch(类型1 参数1) { //针对类型1的异常处理 } catch (类型2 参数2) { //针对类型2的异常处理 } … catch (类型n 参数n) { //针对类型n的异常处理 }
12.1异常处理的机制 ● 请看下面的程序段给出try块与 catch子句的关系: int maino inta9]={123456789},b[9]={0}i stack stack 8) try t for(i=0; i)t cerr<<"栈满”<<endl for(i=0; i<9; i++)cout<<b[]<<'ti cout<<endl 这里有一个try块,对应压栈的操作语句;也有一个 catch子句 ( catch clause),分别处理压栈时的栈满溢出的异常处理
12.1 异常处理的机制 请看下面的程序段给出try块与catch子句的关系: int main() { int a[9]={1,2,3,4,5,6,7,8,9}, b[9]={0}, i; stack istack(8); try { for(i=0; i ) { cerr<<”栈满”<<endl; } for(i=0; i<9; i++) cout<<b[i]<<’\t’; cout<<endl; } 这里有一个try块,对应压栈的操作语句;也有一个catch子句 (catch clause),分别处理压栈时的栈满溢出的异常处理
●。。12.1异常处理的机制心回 首先,在C++中异常往往用类(cass)来实现,以栈为例, 异常类声明如下 class pushon Ful!{}//栈满异常 template void Stack:: Push(const T &datat if(IsFullo) throw pushOnFulk(data)i //注意加了括号是构造一个无名对象 elements[++top]=datai 注意 pushan叫是类,C++要求抛出的必须是对象,所以必 须有“(”,即调用构造函数建立一个对象。异常并非总是类对 象, throw表达式也可以抛出任何类型的对象,如枚举、整数 等等。但最常用的是类对象。 throw表达式抛出异常为异常处 理的第一步。在堆栈的压栈和出栈操作中发生错误而抛出的异 常,理所当然地应由调用堆栈的程序来处理
12.1 异常处理的机制 template void Stack::Push(const T &data) { if(IsFull()) throw pushOnFull(data); //注意加了括号,是构造一个无名对象 elements[++top]=data; } 注意pushOnFull是类,C++要求抛出的必须是对象,所以必 须有“()”,即调用构造函数建立一个对象。异常并非总是类对 象,throw表达式也可以抛出任何类型的对象,如枚举、整数 等等。但最常用的是类对象。throw表达式抛出异常为异常处 理的第一步。在堆栈的压栈和出栈操作中发生错误而抛出的异 常,理所当然地应由调用堆栈的程序来处理。 首先,在C++中异常往往用类(class)来实现,以栈为例, 异常类声明如下: class pushOnFull{...}; //栈满异常
●● 12.1异常处理的机制 由 catch字句捕获并处理异常是第二步。注意与 catch语 句分别匹配的是在压栈成员函数模板中的 throw语句,一个抛 出 pushonFt叫类的无名对象 程序按下列规则控制 1.如果没有异常发生,继续执行ty块中的代码,与try块相关 联的 catch子句被忽略,程序正常执行,main(返回0 2.当第一个try块在for循环中抛出异常,则该for循环退出, try块也退出,去执行 pushan叫异常的 catch子句。 stack. Printstack()不再执行,被忽略。 3.当某条语句抛出异常时,跟在该语句后面的语句将被跳过。 程序执行权交给处理异常的 catch子句,如果没有 catch子句能 够处理异常,则交给C++标准库中定义的 terminate()
12.1 异常处理的机制 程序按下列规则控制: 1.如果没有异常发生,继续执行try块中的代码,与try块相关 联 的catch子句被忽略,程序正常执行,main()返回0。 2.当第一个try块在for循环中抛出异常,则该for循环退出, try 块 也 退 出 , 去执行 pushOnFull 异 常 的 catch 子 句 。 istack.PrintStack()不再执行,被忽略。 3.当某条语句抛出异常时,跟在该语句后面的语句将被跳过。 程序执行权交给处理异常的catch子句,如果没有catch子句能 够处理异常,则交给C++标准库中定义的terminate()。 由catch字句捕获并处理异常是第二步。注意与catch语 句分别匹配的是在压栈成员函数模板中的throw语句,一个抛 出pushOnFull类的无名对象
。。。122捕羲异常 class pushonFull t T vala public: pushan叫(i{val=i} T valueoi return value; y 新的私有数据成员va保存那些不能被压入栈中的值。该值即 调用构造函数时的实参
12.2 捕获异常 catch子句由三部分组成:关键字catch、圆括号中的异常声 明以及复合语句中的一组语句。 * 注意这不是函数,所以圆括 号中不是形参,而是一个异常类型声明,可以是类型也可以是 对象。 异常声明中也可以是一个对象声明。以栈为例。当栈满时, 要求在异常对象中保存不能被压入到栈中的值,这时, pushOnFull类可定义如下: template class pushOnFull { T val; public: pushOnFull(T i) { val = i; } T value() { return _value; } }; 新的私有数据成员 val 保存那些不能被压入栈中的值。该值即 调用构造函数时的实参
122捕获异常 对应在 throw表达式中,构造抛出对象也要有实参: throw pushon Full( datai data即Push( const&data)中的参数data 这样在 catch子句中,要取得va,须调用 pushon Fu中的成 员函数 value: catch (pushOnFulk eobjt cerr<<”栈满”<<e0 bj value0<<”未压入 栈”<<endl return 1 } 在 catch子句的异常声明中声明了对象eobj,用它来调用 pushon Fu类的对象成员函数 value(。异常对象是在抛出 点被创建,与 catch子句是否显式要求创建一个异常对象无关, 该对象总是存在,在 catch子句中只是为了调用异常处理对象 的成员函数才声明为对象,不用类
12.2 捕获异常 这样在catch子句中,要取得 val,须调用pushOnFull中的成 员函数value(): catch(pushOnFull eObj){ cerr<<” 栈 满 ” <<eObj.value()<<” 未压入 栈”<<endl; return 1; } 在catch子句的异常声明中声明了对象eObj,用它来调用 pushOnFull类的对象成员函数value()。异常对象是在抛出 点被创建,与catch子句是否显式要求创建一个异常对象无关, 该对象总是存在,在catch子句中只是为了调用异常处理对象 的成员函数才声明为对象,不用类。 对应在throw表达式中,构造抛出对象也要有实参: throw pushOnFull(data); //data即Push(const &data)中的参数data
●。。123异常处理的详细处理流裎 异常处理的执行过程如下: (1)控制通过正常的顺序执行到达try语句,然后执行try内 的保护段 (2)如果在保护段执行期间没有引起异常,那么跟在try块后 的 catch子句就不执行,程序从异常被抛掷的try块后跟随的 最后一个 catch子句后面的语句继续执行下去。 (3)如果在保护段执行期间或在保护段调用的任何函数中有 异常被抛掷,则从通过 throw运算数创建的对象中创建一个 异常对象。编译器从能够处理抛掷类型的异常的更高执行上 下文中寻找一个 catch子句(或一个能处理任何类型异常的 catch处理程序)。 catch处理程序按其在try块后出现的顺 序被检查。如果没有找到合适的处理程序,则继续检查下 个动态封闭的try块。此处理继续下去直到最外层的封闭try 块被检查完
12.3 异常处理的详细处理流程 异常处理的执行过程如下: (1)控制通过正常的顺序执行到达try语句,然后执行try块内 的保护段。 (2)如果在保护段执行期间没有引起异常,那么跟在try块后 的catch子句就不执行,程序从异常被抛掷的try块后跟随的 最后一个catch子句后面的语句继续执行下去。 (3)如果在保护段执行期间或在保护段调用的任何函数中有 异常被抛掷,则从通过throw运算数创建的对象中创建一个 异常对象。编译器从能够处理抛掷类型的异常的更高执行上 下文中寻找一个catch子句(或一个能处理任何类型异常的 catch处理程序)。catch处理程序按其在try块后出现的顺 序被检查。如果没有找到合适的处理程序,则继续检查下一 个动态封闭的try块。此处理继续下去直到最外层的封闭try 块被检查完