⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 13章 异常处理.txt

📁 C++大学教程txt版中文版 C++大学教程txt版中文版
💻 TXT
📖 第 1 页 / 共 5 页
字号:
Enter tow integers {end-of-file to end):

                                    图 13.1 简单的异常处理例子:除数为0的异常

    第一个输出显示执行成功。第二个除数为0,程序发现错误并发出错误消息。
    现在考虑main中的驱动程序。程序提示并输入两个整数。注意numbcr1和number2的局部声明。
    然后程序继续执行try块,其中的代码可能抛出异常。注意try块中并没有显式列出可能造成错误的实际除法,而是通过quotient函数调用实际除法的代码。函数quotient实际抛出除数为0的异常对象,将在稍后介绍。一般来说,错误可能通过娜块中的代码体现,可能通过函数调用体现,也可能通过try块的代码中启动的深层嵌套函数调用体现。
    try块后面是个catch块,包含除数为0的异常的异常处理器。一般来说,try块中抛出异常时,catch块捕获这个异常,表明符合所抛出异常的相应类型。图13.1中,catch块指定捕获DivideByZeroException类型的异常对象,这种对象符合函数quotient中抛出的异常对象。该异常处理器发出一个错误消息并返回,这里返回1表示因为错误而终止。异常处理器也可以更加复杂。
    执行时,如果try块中的代码设有抛出异常.则立即跳过try块后面的所有catch异常处理器,执行catch异常处理器后面的第一条语句,图13.1中执行return语句,返回0表示正常终止。
    下面看看DivideByZeroExeeption类和quotient函数的定义。在函数quotient中,if语句确定除数为O时.U语句体发出一个throw语句,指定异常对象的构造函数名。这样就生成DivideByZemEx~ption的类对象。try块之后的catch语句(指定类型DivideByZeroException)捕获这个对象。
DivideByZemExeeption类的构造函数只是将message数据成员指向字符串”Dividebyzero”。catch处理器指定的参数(这里为参数error)中接收抛出的对象,井通过调用public访问函数printMessage打印这个消息。

    编程技巧13.4
    将每种执行时错误与相应命名的异常对象相关联能提高程序的清晰性。

13. 6  抛出异常  
    throw关键字表示发生了异常,称为抛出异常。throw通常指定一个操作数(我们将介绍不指定操作数的特殊情况)。throw的操作数可以是任何类型,如果操作数是个对象,则称为异常对象。也可以抛出条件表达式而不是抛出对象,可以抛出不用于错误处理的对象。
    抛出异常时,指定相应类型的最近一个异常处理器(对抛出该异常的try块)捕获这个异常。try块的异常处理紧接在try块后面。
    抛出异常时,生成和初始化throw操作数的一个临时副本,然后这个临时对象初始化异常处理器中的参数。异常处理器执行完毕和退出时,删除临时对象。

    软件工柱视点13.5
    如果需要传递导致异常的错误信息,则可以把这种信息放在抛出对象中。catch处理器包含引用该信息的参数名。

    软件工程视点13.6
    也可以抛出对象而不传递信息,这时只要知道抛出这种类型的对象提供了异常处理器完成工作所需的足够信息。

    抛出异常时,控制退出当前try块,进入try块后面相应的catch处理器(如果有)。抛出点也可能深深嵌套在try块内,控制仍将传入这个catch处理器;抛出点也可以深深嵌套在函数调用中,控制也会转人这个catch处理器。
    try块可能不包含错误检查和throw语句,但try块中所指的代码可能导致执行构造函数中的错误检查代码。try块代码可能对数组类对象加上数组下标,其operator[]成员函数可以重载成抛出下标超界错误的异常。任何函数调用均可调用可能抛出异常的代码或另一可能抛出异常的函数。
    尽管异常可以终止程序执行,但也不一定需要终止程序执行。但异常会终止异常所在的程序块。
   常见编程错误11.3
    异常只能在try决中抛出,如果在try块之外抛出异常,则可能调用terminate。
  
    常见编程错误13.4
    可以抛出条件异常。但一定要小心.因为上升规则可能使条件表达式返回的值与所要类型不同。例如,从同一条件表达式抛出int或double时,条件表达式将int变成double。因此,结果总是由参数为double的处理器捕获,而不是有时由参数为double的处理器捕获,有时由参数为int的处理器捕获。

13.7  捕获异常
   异常处理器放在catch块中。每个catch块以关键字catch开始,接着是括号内包含的类型(表示该块处理的异常类型)和可选参敷名.后面是用花括号括起来的描述异常处理器的代码。捕获异常时,执行catch块中的代码。
    catch处理器定义自己的范围。catch在括号中指定要捕获的对象类型。cateh处理器中的参数可以命名也可以无名。如果是命名参数,则可以在处理器中引用这个参数。如果是无名参数(只指定匹配抛出对象类型的类型),则信息不从抛出点传递到处理器中,只是把控制从抛出点转到处理器中.许多异常都可以这样。

    常见编程错误1 3.5
    指定逗号分开的catch参数表是个语法错误。

    抛出异常对象类型与catch处理器参数类型相符时,执行该类型的catch块,即执行该类型的异常处理器。
    catch处理器中在当前活动try块后面第一个符合所抛出对象的处理器捕获异常。稍后将介绍匹配规则。
    没有捕获的异常调用terminate(默认调用abort终止程序。也可以指定自定义行为,在set_terminate函数调用中指定函数名参数,从而执行另一个函数。
    catch后面是括号和省略号:
    carch(...)
表示捕获所有异常。

    常见编程错误11.6
    将catch(...)放在其他catch块前面时,其他块根本无法执行。catch(...)总是作为try块后面的处理器列表中最后一个处理器。

    软件工程视点1 2.7
    用catch(...)捕获异常的一个缺点是通常无法确定异常类型。另一个缺点是没有命名参数,就无法在异常处理器中引用异常对象。

  也许某个抛出对象没有任何匹配的异常处理器。这时匹配搜索会继续到外面一层try块。这个过程一直继续,也许最终还是没有任何匹配的异常处理器。这时调用terminate(默认调用abort)终止程序。
    异常处理器按顺序搜索,寻找匹配项,并执行第一个匹配的处理器。处理器执行完毕时,控制恢复到最后一个catch块后面的第一条语句,即该try块中最后一个异常处理器后面的第一条语句。
    也许几个异常都匹配所抛出的对象。这时执行第一个匹配的异常处理器。如果几个异常处理器都匹配所抛出的对象,而每个异常处理器用不同方法处理异常,则处理器的顺序会影响处理异常的方法。
    也许几个catch处理器中都包含匹配所抛出对象的类类型。这可能有几个原因:第一,有一个捕获任何异常的catch(…)处理器。第二,由于继承层次,派生类对象可以由派生类类型的异常处理器和基类类型的异常处理器捕获。

    常见编程错误:13.7
    将捕获基类类型的异常处理器放在捕获派生类类型的异常处理器之前是个逻辑错误。基类类型的异常处理器捕获从该类派生的所有对象.因此根本不会执行派生类类型的异常处理器。

    测试与调试提示13.1
    程序员确定异常处理器列出的顺序。这个顺序可能影响try块中所产生异常的处理方法。如果程序处理异常时出现意外行为,可能是前面的catch块截获并处理了这个异常,使其没有被所需的异常处理器处理。

    有时程序可能处理许多密切相关的异常类型。这时不是提供不同的异常类和catch处理器,而是用一个异常类和catch处理器处理一组异常。发生每个异常时,可以生成具有不同private数据的异常对象。cateh处理器通过检查private数据区分异常的类型。
    何时发生匹配呢?下列情况下,catch处理器参数匹配所抛出对象的类型:
    ●实际是同一类型。
    ●cateh处理器参数类型是所抛出对象类型的Public基类。
    ●处理器参数为基类指针或引用类型,而抛出对象为派生类指针或引用类型。
    ●cateh处理器为cateh(...)。

    常见编程错误13.8
    将带void *参数类型的异常处理器放在具有其他指针类型的异常处理器前面是个逻辑错误。void*处理器捕获所有指针类型的异常,因此其他异常处理器根本不可能执行。

    需要有准确的类型匹配。寻找处理器时只允许派生类向基类转换,而不允许其他转换和升级。
    可以抛出const对象。这时catch处理器参数类型也应声明为const。
    默认情况下,如果异常没有相应的处理器,则程序终止。尽管这应该是正确的做法,但程序员通常不这样做,从而造成错误,使得程序继续执行。
    一个try块后面跟几个cateh块类似于switch语句。不必用break退出异常处理器(跳过其余异常处理器)。每个catceh块定义不同的范围,而switch浯句中的所有case都在switch的范围中。

    常见编程错误13.9
    将分号放在try块后面或try块后面的任何catch处理器(除最后一个catch处理器)后面是个语法错误。

 异常处理器无法访问try块中定义的自动化对象,因为发生异常时try块终止,try中的所有自动对象均在处理器开始执行之前删除。
    异常处理器中发生异常时会出现什么情况呢?异常处理器开始执行时,原先捕获的异常正在处理。因此异常处理器中发生异常时应在抛出原异常的try块之外处理。
    异常处理器可以用不同的方式编写,可以检查错误和确定调用terminate;可以再抛出异常(见13 8节);可以将一种异常变为另一种异常,抛出不同异常;可以进行必要的恢复,并恢复执行最后一个异常处理器之后的语句;可以检查错误原因、删除错误原因和重新调用原先导致异常的函数(这不会生成无穷递归);可以向程序环境返回一些状态值等等。

    软件工程视点13.8
    最好在设计过程中把异常处理策略加进系统,后面要再把异常处理策略加进第系统是很困难的。

    try块不抛出任何异常而正常地执行完毕时,控制传入try块后面最后一个catch处理器之后的第一条浯句。
    在catch处理器中用return语句无法返回抛出点。这种return语句只是返回调用catch块所在函数的函数。

    常兄编程错误13. 10
    如果认为处理异常后控制会返回throw后面妁第一条语句是个逻辑错误。

    软件工程视点13.9
    传统控制流程不用异常的另一个原因是这些“增加的”异常可能搞乱真正的错误型异常。程序员更难跟踪异常种类。例如,程序处理大量异常时,无法确定catch(...)捕获的是什么异常。异常情况只能针对不常见的极少数情况。

    捕获异常时,try块中可能有已经分配而还没有释放的资源。如果可能,cateh处理器应释放这些资源。例如,catch处理器删除new分配的空间和关闭抛出异常的try块中打开的任何文件。
    catch处理器处理错误之后可以让程序继续正确执行,也可以终止程序。
    catch处理器本身也可以发现错误和抛出异常。这种异常不是由抛出异常的catch处理器所在try块中的catth处理器处理,而是由外层try块中的相关catch处理器处理。

    常见编程错误13. 11
    假设catch处理器抛出的异常由该处理器处理,或由抛出异常的try决中相关的处理器(导致执行原先的catch处理器)处理是个逻辑错误。

13.8  再抛出异常
    捕获异常的处理器也可以决定不处理异常或释放资源,然后让其他处理器处理这个异常。这时,处理器只要再抛出异常,如下所示:
    throw;
这种不带参数的throw再抛出异常。如果开始没有抛出异常,则再抛出异常调用terminate。

    常见编程错误13.12
    将空throw语句放在catch处理器之外,执行这种throw会调用terminate。即使处理器能处理异常,不管这个异常是否进行处理,处理器仍然可以再抛出异常以便在处理器之外继续处理。再抛出异常由外层try块检测,由所在try块之后列出的异常处理器处理。

    软件工程视点13.10
    用catch(...)进行与异常类型无关的恢复,如释放共用资源。可以再抛出异常以警示更具体的外展catch块。

    图13.2的程序演示了再抛出异常。在main第26行的try块中,调用函数throwwExcepion。在函数throwException的try块中,第13行的throw语句抛出标准库类exception的实例(在头文件<exception>中定义)。这个异常在第15行的catch处理器中立即捕获,打印一个错误消息,然后再抛出异常。因此终止函数throwException并将控制返回main中的try/catch块。第30行再次捕获异常并打印一个错误消息。
1 // Fig. 13.2: fig13_02.cpp
2 // Demonstration of rethrowing an exception.
3 #include <iostream>
4 #include <exception>
5
6 using namespace std;
7
8 void throwException() throw ( exception )
9{
10   // Throw an exception and immediately catch it.
11   try {
12     cout << "Function throwException\n";
13     throw exception();  // generate exception
14   }
15   catch( exception e )
16   {
17     cout << "Exception handled in function throwException\n";
18     throw;  // rethrow exception for further processing
19   }
2O
21   cout << "This also should not print\n";
22 }
23
24 int main()
25 {
26   try {
27     throwException();
28     cout << "This should not print\n";
29   }
30   catch ( exception e )
31   {
32     cout << "Exception handled in main\n";
33   }
34
35   cout << "Program control continues after catch in main"
36        << endl;
37   return 0;
38 }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -