15 Exception handling [except]
1 Exception handling provides a way of transferring control and information from a point in the execution of a program to an exception handler associated with a point previously passed by the execution. A handler will be invoked only by a throw-expression invoked in code executed in the handler’s try block or in functions called from the handler’s try block .
try-block:
try compound-statement handler-seq
function-try-block:
try ctor-initializeropt function-body handler-seq
handler-seq:
handler handler-seqopt
handler:
catch ( exception-declaration ) compound-statement
exception-declaration:
type-specifier-seq declarator
type-specifier-seq abstract-declarator
type-specifier-seq
...
throw-expression:
throw assignment-expressionopt
A try-block is a statement (clause 6). A throw-expression is of type void. Code that executes a throwexpression is said to “throw an exception;” code that subsequently gets control is called a “handler.” [Note: within this clause “try block” is taken to mean both try-block and function-try-block. ]
2 A goto or switch statement shall not be used to transfer control into a try block or into a handler.
[Example:
void f() {
goto l1; // Ill-formed
goto l2; // Ill-formed
try {
goto l1; // OK
goto l2; // Ill-formed
l1: ;
} catch (...) {
l2: ;
goto l1; // Ill-formed
goto l2; // OK
}
}
—end example] A goto, break, return, or continue statement can be used to transfer control out of a try block or handler. When this happens, each variable declared in the try block will be destroyed in the context that directly contains its declaration. [Example:
lab: try {
T1 t1;
try {
T2 t2;
if (condition)
{
goto lab;
}
}
catch(...)
{
/* handler 2 */
}
}
catch(...)
{
/* handler 1 */
}
Here, executing goto lab; will destroy first t2, then t1, assuming the condition does not declare a variable. Any exception raised while destroying t2 will result in executing handler 2; any exception raised while destroying t1 will result in executing handler 1. ]
3 A function-try-block associates a handler-seq with the ctor-initializer, if present, and the function-body. An exception thrown during the execution of the initializer expressions in the ctor-initializer or during the execution of the function-body transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers. [Example:
int f(int);
class C {
int i;
double d;
public:
C(int, double);
};
C::C(int ii, double id)
try
: i(f(ii)), d(id)
{
// constructor function body
}
catch (...)
{
// handles exceptions thrown from the ctor-initializer
// and from the constructor function body
}—end example]
1. 通过异常处理机制,程序控制流可以从异常发生点直接跳转到异常处理模块。通过显示调用throw语句可以触发这样的跳转。Throw语句必须处于try模块中,或者位于try模块中被调用函数的函数体内。
( 此处略去异常处理的相关语法)
从整体的角度来看,try模块可以被看作是一个语句(请看下面的相关注解)。throw表达式的返回类型是void类型。当我们调用throw语句产生一个异常时,我们称之为”抛出一个异常”。抛出异常后,程序控制流随后跳转所至的模块被称为”异常处理模块”。[注意:此处的短语”try模块”既指普通的”try模块”,也包括”函数try模块”]
2. 我们不应该通过goto或者switch语句强行地将程序控制流跳转到 “try模块” 或者 “异常处理模块”中去。
[Example: void f() { goto l1; // 糟糕的风格 goto l2; // 糟糕的风格 try { goto l1; // OK goto l2; // 糟糕的风格 l1: ; } catch (...) { l2: ; goto l1; // 糟糕的风格 goto l2; // OK } } —end example] |
不过,goto, break, return, 或者 continue 这些语句可以被用来跳出“try模块”或者是“异常处理模块”。一旦程序控制流跳出了“try模块”或者是“异常处理模块”,那么在这些模块中声明的局部变量都将被销毁(此处并没有按照英文原文翻译,请参考英文原文)。
lab: try { T1 t1; try { T2 t2; if (condition) { goto lab; } } catch(...) { /* handler 2 */ } } catch(...) { /* handler 1 */ } |
在上面的例子中,当我们执行goto lab语句之后, 变量t2首先被销毁,然后是变量 t1,这里我们假设 condition没有声明额外的变量。如果在销毁t2的时候发生了异常,程序将会跳转到 “handler 2” 模块;如果在销毁t1的时候出了异常,那么程序控制流就会跳转到”handler 1 ” 模块。
3. “函数try模块”既可以捕捉到函数体内发生的异常,也可以捕捉到构造函数初始化列表中产生的异常。当构造函数初始化列表或者函数体抛出一个异常时,程序控制流将会跳转到相应的”异常处理模块”中去,这和普通的”try模块”的程序控制流跳转过程是一样的。
[Example:
int f(int); class C { int i; double d; public: C(int, double); }; C::C(int ii, double id) try : i(f(ii)), d(id) // 构造函数的初始化列表 { // 构造函数的函数体 } catch (...) { // 如果 构造函数初始化列表 或者 函数体 抛出了一个异常 // 程序控制流将会跳转到这里 }—end example]
|
相关注解:
l 在C++标准中, try-block 的确被定义为一个statement。下面是相关的定义:
statement:
labeled-statement
expression-statement
compound-statement
selection-statement
iteration-statement
jump-statement
declaration-statement
try-block
l X-seq is one or more X’s without intervening delimiters (e.g. declaration-seq is a sequence of declarations).通过上面的解释可以看出,handler-seq指的是一个或者多个连续的异常处理模块,中间不掺杂其他的语法单位。下面是多个连续的异常处理模块的例子:
catch(int &i ) { } catch(char *ch ) { } catch(...) { }
|
l ctor-initializeropt 指的是构造函数初始化列表,下标opt意思是该项为可选项。下面给出的是一个构造函数的函数try模块 的范例。
class A { public: A() Try ç 请注意try关键字所处的位置,其位于右括号之后,初始化列表的 : 之前 : _k(0) { // 构造函数,在这个例子中,我们什么也没有做 } catch(...) { }
int _k; }; |