try块和异常处理(摘自c++ primer)

来源:互联网 发布:中国国际人工智能峰会 编辑:程序博客网 时间:2024/06/05 19:02

一.try  块和异常处理 

   在设计各种软件系统的过程中,处理程序中的错误和其他反常行为是困难的部分之一。像通信交换机和路由器这类长期运行的交互式系统必须将 90% 的程序代码用于实现错误检测和错误处理。随着基于 Web 的应用程序在运行时不确定性的增多,错误的处理变得越来越重要。 

   异常就是运行时出现的不正常,例如运行时耗尽了内存或遇到意外的非法输入。异常存在于程序的正常功能之外,并要求程序立即处理。 在设计良好的系统中,异常是程序错误处理的一部分。当程序代码检查到无法处理的问题时,异常处理就特别有用。在这些情况下,检测出问题的那部分程序需要一种方法把控制权转到可以处理这个问题的那部分程序。错误检测程序还必须指出具体出现了什么问题,并且可能需要提供一些附加信息。 异常机制提供程序中错误检测与错误处理部分之间的通信。C++ 的异常处理中包括: 

1.throw表达式,错误检测部分使用这种表达式来说明遇到了不可处理的错误。可以说,throw 引发了异常条件。 

2.try块,错误处理部分使用它来处理异常。try 语句块以 try 关键字开始,并以一个或多个 catch 子句结束。在 try 块中执行的代码所抛出(throw)的异常,通常会被其中一个 catch 子句处理。由于它们“处理”异常,catch 子句也称为处理代码。 

3.由标准库定义的一组异常类,用来在 throw 和相应的 catch 之间传递有关的错误信息。

(1)throw表达式 

   系统通过 throw 表达式抛出异常。throw 表达式由关键字 throw 以及尾随的表达式组成,通常以分号结束,这样它就成为了表达式语句。throw 表达式的类型决定了所抛出异常的类型。 回顾第 1.5.2 节将两个 Sales_item 类型对象相加的程序,就是一个简单的例子。该程序检查读入的记录是否来自同一本书。如果不是,就输出一条信息然后退出程序。 

     Sales_item item1, item2; 

     std::cin >> item1 >> item2; 

     // first check that item1 and item2 represent the same book 

     if (item1.same_isbn(item2)) { 

         std::cout << item1 + item2 << std::endl; 

         return 0; // indicate success 

     } else { 

         std::cerr << "Data must refer to same ISBN" 

                   << std::endl; 

         return -1; // indicate failure 

     } 

 

在使用 Sales_items 的更简单的程序中,把将对象相加的部分和负责跟用户交互的部分分开。在这个例子中,用 throw 抛出异常来改写检测代码: 

     // first check that data is for the same item 

     if (!item1.same_isbn(item2)) 

         throw runtime_error("Data must refer to same ISBN"); 

     // ok, if we're still here the ISBNs are the same 

     std::cout << item1 + item2 << std::endl; 

 

这段代码检查 ISBN 对象是否不相同。如果不同的话,停止程序的执行,并将控制转移给处理这种错误的处理代码。 throw 语句使用了一个表达式。在本例中,该表达式是 runtime_error 类型的对象。runtime_error 类型是标准库异常类中的一种,在 stdexcept 头文件中定义。在后续章节中很快就会更详细地介绍这些类型。我们通过传递 string 对象来创建 runtime_error 对象,这样就可以提供更多关于所出现问题的相关信息。

(2)try块 

try块的通用语法形式是: 

     try { 

         program-statements 

     } catch (exception-specifier) { 

         handler-statements 

     } catch (exception-specifier) { 

         handler-statements 

     } //... 

try 块以关键字 try 开始,后面是用花括号起来的语句序列块。try 块后面是一个或多个 catch 子句。每个catch 子句包括三部分:关键字 catch,圆括号内单个类型或者单个对象的声明——称为异常说明符,以及通常用括号括起来的语句块。如果选择了一个 catch 子句来处理异常,则执行相关的块语句。一旦 catch 子句执行结束,程序流程立即继续执行紧随着最后一个 catch 子句的语句。 try 语句内的 program-statements 形成程序的正常逻辑。这里面可以包含任意 C++ 语句,包括变量声明。与其他块语句一样,try 块引入局部作用域,在 try 块中声明的变量,包括 catch 子句声明的变量,不能在 try 外面引用。在前面的例子中,使用了 throw 来避免将两个表示不同书的 Sales_items 对象相加。想象一下将 Sales_items 对象相加的那部分程序与负责与用户交流的那是分开的,则与用户交互的部分也许会包含下面的用于处理所捕获异常的代码: 

     while (cin >> item1 >> item2) { 

         try { 

             // execute code that will add the two Sales_items 

             // if the addition fails, the code throws a runtime_error 

exception 

         } catch (runtime_error err) { 

             // remind the user that ISBN must match and prompt for 

another pair 

             cout << err.what() 

                  << "\nTry Again? Enter y or n" << endl; 

             char c; 

             cin >> c; 

             if (cin && c == 'n') 

                 break;     // break out of the while loop 

         } 

     } 

 

关键字 try 后面是一个块语句。这个块语句调用处理 Sales_item 对象的程序部分。这部分也可能会抛出 runtime_error 类型的异常。 上述 try 块提供单个 catch 子句,用来处理 runtime_error 类型的异常。在执行 try 块代码的过程中,如果在 try 块中的代码抛出 runtime_error 类型的异常,则处理这类异常的动作在 catch 后面的块语句中定义。本例中,catch 输出信息并且询问用户是否继续进行异常处理。如果用户输入'n',则结束 while;否则继续循环,读入两个新的 Sales_items 对象。 通过输出 err.what() 的返回值提示用户。大家都知道 err 返回 runtime_error 类型的值,因此可以推断出 what 是 runtime_error 类的一个成员函数(1.5.2 节)。每一个标准库异常类都定义了名为 what 的成员函数。这个函数不需要参数,返回 C 风格字符串。在出现 runtime_error 的情况下,what 返回的 C 风格字符串,是用于初始化 runtime_error 的 string 对象的副本。如果在前面章节描述的代码抛出异常,那么执行这个 catch 将输出

     Data must refer to same ISBN 

     Try Again? Enter y or n 

 

(3)函数在寻找处理代码的过程中退出 

    在复杂的系统中,程序的执行路径也许在遇到抛出异常的代码之前,就已经经过了多个 try 块。例如,个 try 块可能调用了包含另一 try 块的函数,它的 try 块又调用了含有 try 块的另一个函数,如此类推。 寻找处理代码的过程与函数调用链刚好相反。抛出一个异常时,首先要搜索的是抛出异常的函数。如果没有找到匹配的 catch,则终止这个函数的执行,并在调用这个函数的函数中寻找相配的 catch。如果仍然找到相应的处理代码,该函数同样要终止,搜索调用它的函数。如此类推,继续按执行路径回退,直到找到适当类型的 catch 为止。 如果不存在处理该异常的 catch 子句,程序的运行就要跳转到名为 terminate  的标准库函数,该函在 exception 头文件中定义。这个标准库函数的行为依赖于系统,通常情况下,它的执行将导致程序非正常退出。 在程序中出现的异常,如果没有经 try 块定义,则都以相同的方式来处理:毕竟,如果没有任何 try 块,也就没有捕获异常的处理代码(catch 子句)。此时,如果发生了异常,系统将自动调用 terminate 终止程序的执行。