## 5.3 出錯處理 (Exception handling)

本節介紹的出錯處理是ANSI-C++ 標準引入的新功能。如果你使用的C++ 編譯器不兼容這個標準,則你可能無法使用這些功能。
在編程過程中,很多時候我們是無法確定一段代碼是否總是能夠正常工作的,或者因為程序訪問了并不存在的資源,或者由于一些變量超出了預期的范圍,等等。
這些情況我們統稱為出錯(例外),C++ 新近引入的三種操作符能夠幫助我們處理這些出錯情況: try, throw 和 catch 。
它們的一般用法是:
~~~
try {
// code to be tried
throw exception; }
catch (type exception) {
// code to be executed in case of exception
}
~~~
它們所進行的操作是:
* try 語句塊中的代碼被正常執行。如果有例外發生,代碼必須使用關鍵字throw 和一個參數來扔出一個例外。這個參數可以是任何有效的數據類型,它的類型反映了例外的特征。
* 如果有例外發生,也就是說在try 語句塊中有一個throw 指令被執行了,則catch 語句塊會被執行,用來接收throw傳來的例外參數。
例如:
~~~
// exceptions
#include <iostream.h>
int main () {
char myarray[10];
try {
for (int n=0; n<=10; n++) {
if (n>9) throw "Out of range";
myarray[n]='z';
}
} catch (char * str) {
cout << "Exception: " << str << endl;
}
return 0;
}
~~~
| Exception: Out of range |
在這個例子中,如果在n 循環中,n 變的大于9 了,則仍出一個例外,因為數組 myarray[n] 在這種情況下會指向一個無效的內存地址。當throw 被執行的時候,try 語句塊立即被停止執行,在try 語句塊中生成的所有對象會被銷毀。此后,控制權被傳遞給相應的catch 語句塊(上面的例子中即執行僅有的一個catch)。最后程序緊跟著catch 語句塊繼續向下執行,在上面的例子中就是執行 return 0;.
throw 語法與 return 相似,只是參數不需要擴在括號。
catch 語句塊必須緊跟著try 語句塊后面,中間不能有其它的代碼。catch 捕獲的參數可以是任何有效的數據類型。catch 甚至可以被重載以便能夠接受不同類型的參數。在這種情況下被執行catch 語句塊是相應的符合被throw扔出的參數類型的那一個:
~~~
// exceptions: multiple catch blocks
#include <iostream.h>
int main () {
try {
char * mystring;
mystring = new char [10];
if (mystring == NULL) throw "Allocation failure";
for (int n=0; n<=100; n++) {
if (n>9) throw n;
mystring[n]='z';
}
} catch (int i) {
cout << "Exception: ";
cout << "index " << i << " is out of range" << endl;
} catch (char * str) {
cout << "Exception: " << str << endl;
}
return 0;
}
~~~
| Exception: index 10 is out of range |
在上面的例子中,有兩種不同的例外可能發生:
1. 如果要求的10個字符空間不能夠被賦值(這種情況很少,但是有可能發生):這種情況下扔出的例外會被catch (char * str)捕獲。
2. n超過了mystring的最大索引值(index):這種情況下扔出的例外將被catch (int i)捕獲,因為它的參數是一個整型值。
我們還可以定義一個catch 語句塊來捕獲所有的例外,不論扔出的是什么類型的參數。這種情況我們需要在catch 或面的括號中寫三個點來代替原來的參數類型和名稱,如:
~~~
try { // code here
}
catch (...) {
cout << "Exception occurred";
}
~~~
try-catch 也是可以被嵌套使用的。在這種情況下,我們可以使用表達式throw;(不帶參數)將里面的catch 語句塊捕獲的例外傳遞到外面一層,例如:
~~~
try { try { // code here
}
catch (int n) {
throw;
}
}
catch (...) {
cout << "Exception occurred";
}
~~~
### 沒有捕獲的例外 (Exceptions not caught)
如果由于沒有對應的類型,一個例外沒有被任何catch 語句捕獲,特殊函數terminate 將被調用。
這個函數通常已被定義了,以便立即結束當前的進程(process),并顯示一個“非正常結束”( "Abnormal termination")的出錯信息。它的格式是:
`void terminate();`
### 標準例外 (Standard exceptions)
一些C++ 標準語言庫中的函數也會扔出一些列外,我們可以用try 語句來捕獲它們。這些例外扔出的參數都是std::exception 引申出的子類類型的。這個類(std::exception) 被定義在C++ 標準頭文件 中,用來作為exceptions標準結構的模型:

因為這是一個類結構,如果你包括了一個catch 語句塊使用地址(reference)來捕獲這個結構中的任意一種列外 (也就是說在類型后面加地址符 &),你同時可以捕獲所有引申類的例外 (C++的繼承原則)。
下面的例子中,一個類型為 bad_typeid 的例外(exception的引申類),在要求類型信息的對象為一個空指針的時候被捕獲:
~~~
// standard exceptions
#include <iostream.h>
#include <exception>
#include <typeinfo>
class A {virtual void f() {}; };
int main () {
try {
A * a = NULL;
typeid (*a);
} catch (std::exception& e) {
cout << "Exception: " << e.what();
}
return 0;
}
~~~
| Exception: Attempted typeid of NULL pointer |
你可以用這個標準的例外層次結構來定義的你的例外或從它們引申出新的例外類型。