6.13. try Blocks and Exception Handling6.13. try 块和异常处理Handling errors and other anomalous behavior in programs can be one of the most difficult parts of designing any system. Long-lived, interactive systems such as communication switches and routers can devote as much as 90 percent of their code to error detection and error handling. With the proliferation of Web-based applications that run indefinitely, attention to error handling is becoming more important to more and more programmers. 在设计各种软件系统的过程中,处理程序中的错误和其他反常行为是困难的部分之一。像通信交换机和路由器这类长期运行的交互式系统必须将 90% 的程序代码用于实现错误检测和错误处理。随着基于 Web 的应用程序在运行时不确定性的增多,越来越多的程序员更加注重错误的处理。 Exceptions are run-time anomalies, such as running out of memory or encountering unexpected input. Exceptions exist outside the normal functioning of the program and require immediate handling by the program. 异常就是运行时出现的不正常,例如运行时耗尽了内存或遇到意外的非法输入。异常存在于程序的正常功能之外,并要求程序立即处理。 In well-designed systems, exceptions represent a subset of the program's error handling. Exceptions are most useful when the code that detects a problem cannot handle it. In such cases, the part of the program that detects the problem needs a way to transfer control to the part of the program that can handle the problem. The error-detecting part also needs to be able to indicate what kind of problem occurred and may want to provide additional information. 在设计良好的系统中,异常是程序错误处理的一部分。当程序代码检查到无法处理的问题时,异常处理就特别有用。在这些情况下,检测出问题的那部分程序需要一种方法把控制权转到可以处理这个问题的那部分程序。错误检测程序还必须指出具体出现了什么问题,并且可能需要提供一些附加信息。 Exceptions support this kind of communication between the error-detecting and error-handling parts of a program. In C++ exception handling involves: 异常机制提供程序中错误检测与错误处理部分之间的通信。C++ 的异常处理中包括:
In the remainder of this section we'll introduce these three components of exception handling. We'll have more to say about exceptions in Section 17.1 (p. 688). 在本节接下来的部分将要介绍这三种异常处理的构成。而第 17.1 节将会进一步了解异常的相关内容。 6.13.1. A throw Expression6.13.1 throw 表达式An exception is thrown using a throw expression, which consists of the keyword throw followed by an expression. A throw expression is usually followed by a semicolon, making it into an expression statement. The type of the expression determines what kind of exception is thrown. 系统通过 throw 表达式抛出异常。throw 表达式由关键字 throw 以及尾随的表达式组成,通常以分号结束,这样它就成为了表达式语句。throw 表达式的类型决定了所抛出异常的类型。 As a simple example, recall the program on page 24 that added two objects of type Sales_item. That program checked whether the records it read referred to the same book. If not, it printed a message and exited. 回顾第 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 } In a less simple program that used Sales_items, the part that adds the objects might be separated from the part that manages the interaction with a user. In this case, we might rewrite the test to throw an exception instead: 在使用 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; In this code we check whether the ISBNs differ. If so, we discontinue execution and transfer control to a handler that will know how to handle this error. 这段代码检查 ISBN 对象是否不相同。如果不同的话,停止程序的执行,并将控制转移给处理这种错误的处理代码。 A throw takes an expression. In this case, that expression is an object of type runtime_error. The runtime_error type is one of the standard library exception types and is defined in the stdexcept header. We'll have more to say about these types shortly. We create a runtime_error by giving it a string, which provides additional information about the kind of problem that occurred. throw 语句使用了一个表达式。在本例中,该表达式是 runtime_error 类型的对象。runtime_error 类型是标准库异常类中的一种,在 stdexcept 头文件中定义。在后续章节中很快就会更详细地介绍这些类型。我们通过传递 string 对象来创建 runtime_error 对象,这样就可以提供更多关于所出现问题的相关信息。 6.13.2. The try Block6.13.2. try 块The general form of a try block is try 块的通用语法形式是: try { program-statements } catch (exception-specifier) { handler-statements } catch (exception-specifier) { handler-statements } //... A try block begins with the keyword try followed by a block enclosed in braces. Following the try block is a list of one or more catch clauses. A catch clause consists of three parts: the keyword catch, the declaration of a single type or single object within parentheses (referred to as an exception specifier), and a block, which as usual must be enclosed in curly braces. If the catch clause is selected to handle an exception, the associated block is executed. Once the catch clause finishes, execution continues with the statement immediately following the last catch clause. try 块以关键字 try 开始,后面是用花括号起来的语句序列块。try 块后面是一个或多个 catch 子句。每个 catch 子句包括三部分:关键字 catch,圆括号内单个类型或者单个对象的声明——称为异常说明符,以及通常用花括号括起来的语句块。如果选择了一个 catch 子句来处理异常,则执行相关的块语句。一旦 catch 子句执行结束,程序流程立即继续执行紧随着最后一个 catch 子句的语句。 The program-statements inside the try constitute the normal logic of the program. They can contain any C++ statement, including declarations. Like any block, a try block introduces a local scope, and variables declared within a try block cannot be referred to outside the try, including within the catch clauses. try 语句内的 program-statements 形成程序的正常逻辑。这里面可以包含任意 C++ 语句,包括变量声明。与其他块语句一样,try 块引入局部作用域,在 try 块中声明的变量,包括 catch 子句声明的变量,不能在 try 外面引用。 Writing a Handler编写处理代码In the preceeding example we used a throw to avoid adding two Sales_items that represented different books. We imagined that the part of the program that added to Sales_items was separate from the part that communicated with the user. The part that interacts with the user might contain code something like the following to handle the exception that was thrown: 在前面的例子中,使用了 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 } } Following the try keyword is a block. That block would invoke the part of the program that processes Sales_item objects. That part might throw an exception of type runtime_error. 关键字 try 后面是一个块语句。这个块语句调用处理 Sales_item 对象的程序部分。这部分也可能会抛出 runtime_error 类型的异常。 This try block has a single catch clause, which handles exceptions of type runtime_error. The statements in the block following the catch define the actions that will be executed if code inside the try block throws a runtime_error. Our catch handles the error by printing a message and asking the user to indicate whether to continue. If the user enters an 'n', then we break out of the while. Otherwise the loop continues by reading two new Sales_items. 上述 try 块提供单个 catch 子句,用来处理 runtime_error 类型的异常。在执行 try 块代码的过程中,如果在 try 块中的代码抛出 runtime_error 类型的异常,则处理这类异常的动作在 catch 后面的块语句中定义。本例中,catch 输出信息并且询问用户是否继续进行异常处理。如果用户输入'n',则结束 while;否则继续循环,读入两个新的 Sales_items 对象。 The prompt to the user prints the return from err.what(). We know that err has type runtime_error, so we can infer that what is a member function (Section 1.5.2, p. 24) of the runtime_error class. Each of the library exception classes defines a member function named what. This function takes no arguments and returns a C-style character string. In the case of runtime_error, the C-style string that what returns is a copy of the string that was used to initialize the runtime_error. If the code described in the previous section threw an exception, then the output printed by this catch would be 通过输出 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 functions are exited during the search for a handler函数在寻找处理代码的过程中退出in complex systems the execution path of a program may pass through multiple try blocks before encountering code that actually throws an exception. for example, a try block might call a function that contains a try, that calls another function with its own try, and so on. 在复杂的系统中,程序的执行路径也许在遇到抛出异常的代码之前,就已经经过了多个 try 块。例如,一个 try 块可能调用了包含另一 try 块的函数,它的 try 块又调用了含有 try 块的另一个函数,如此类推。 the search for a handler reverses the call chain. when an exception is thrown, the function that threw the exception is searched first. if no matching catch is found, the function terminates, and the function that called the one that threw is searched for a matching catch. if no handler is found, then that function also exits and the function that called it is searched; and so on back up the execution path until a catch of an appropriate type is found. 寻找处理代码的过程与函数调用链刚好相反。抛出一个异常时,首先要搜索的是抛出异常的函数。如果没有找到匹配的 catch,则终止这个函数的执行,并在调用这个函数的函数中寻找相配的 catch。如果仍然找到相应的处理代码,该函数同样要终止,搜索调用它的函数。如此类推,继续按执行路径回退,直到找到适当类型的 catch 为止。 If no catch clause capable of handling the exception exists, program execution is transferred to a library function named terminate, which is defined in the exception header. The behavior of that function is system dependent, but it usually aborts the program. 如果不存在处理该异常的 catch 子句,程序的运行就要跳转到名为 terminate 的标准库函数,该函数在 exception 头文件中定义。这个标准库函数的行为依赖于系统,通常情况下,它的执行将导致程序非正常退出。 Exceptions that occur in programs that define no try blocks are handled in the same manner: After all, if there are no try blocks, there can be no handlers for any exception that might be thrown. If an exception occurs, then terminate is called and the program (ordinarily) is aborted. 在程序中出现的异常,如果没有经 try 块定义,则都以相同的方式来处理:毕竟,如果没有任何 try 块,也就没有捕获异常的处理代码(catch 子句)。此时,如果发生了异常,系统将自动调用 terminate 终止程序的执行。 6.13.3. Standard Exceptions6.13.3. 标准异常The C++ library defines a set of classes that it uses to report problems encountered in the functions in the standard library. These standard exception classes are also intended to be used in the programs we write. Library exception classes are defined in four headers: C++ 标准库定义了一组类,用于报告在标准库中的函数遇到的问题。程序员可在自己编写的程序中使用这些标准异常类。标准库异常类定义在四个头文件中:
Standard Library Exception Classes标准库异常类The library exception classes have only a few operations. We can create, copy, and assign objects of any of the exception types. The exception, bad_alloc, and bad_cast types define only a default constructor (Section 2.3.4, p. 50); it is not possible to provide an initializer for objects of these types. The other exception types define only a single constructor that takes a string initializer. When we define any of these other exception types, we must supply a string argument. That string initializer is used to provide additional information about the error that occurred. 标准库异常类只提供很少的操作,包括创建、复制异常类型对象以及异常类型对象的赋值。 exception、bad_alloc 以及 bad_cast 类型只定义了默认构造函数(第 2.3.4 节),无法在创建这些类型的对象时为它们提供初值。其他的异常类型则只定义了一个使用 string 初始化式的构造函数。当需要定义这些异常类型的对象时,必须提供一想 string 参数。string 初始化式用于为所发生的错误提供更多的信息。 The exception types define only a single operation named what. That function takes no arguments and returns a const char*. The pointer it returns points to a C-style character string (Section 4.3, p. 130). The purpose of this C-style character string is to provide some sort of textual description of the exception thrown. 异常类型只定义了一个名为 what 的操作。这个函数不需要任何参数,并且返回 const char* 类型值。它返回的指针指向一个 C 风格字符串(第 4.3 节)。使用 C 风格字符串的目的是为所抛出的异常提出更详细的文字描述。 The contents of the C-style character array to which what returns a pointer depends on the type of the exception object. For the types that take a string initializer, the what function returns that string as a C-style character array. For the other types, the value returned varies by compiler. what 函数所返回的指针指向 C 风格字符数组的内容,这个数组的内容依赖于异常对象的类型。对于接受 string 初始化式的异常类型,what 函数将返回该 string 作为 C 风格字符数组。对于其他异常类型,返回的值则根据编译器的变化而不同。 ![]() |