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;
};