C++標準程序庫由不同的成分構成。來源不同,設計與實現風格迥異。而錯誤處理和異常處理正是這種差異的一個典型體現。
1.1 標準異常類別
語言本身或者標準庫所拋出的所有異常,都派生自積累exception.這是其它數個標準異常類別的基類,它們共同構成一個類體系具體如下:
這些標準異常可分爲三組:{1.語言本身支持的異常;2.C++標準程序庫發出的異常;3.程序作用域(scope of a program)之外發出的異常}
exception:
1)bad_alloc
2)bad_cast
3)bad_typied
4)logic_error {domain_error、invalid_argument、length_error、out_of_range}
5)ios_base::failure
6)runtime_error {range_error、overflow_error、underflow_error}
7)bad_exception
1.1.1 語言本身支持的異常
此類異常用以支撐某些語言特性,所以從某種角度來說它們不是標準程序庫的一部分,而是核心語言的一部分。如果以下操作失敗,就會拋出這類異常。
----全局操作符new操作失敗,就會拋出bad_alloc異常(若採用new的nothrow版本,就另當別論)。由於這個異常可能於任何時間在任何複雜的程序中發生,所以可說是最重要的一個異常。
----執行期間,當一個加諸於reference身上的"動態性別轉換操作"失敗時,danamac_cast會拋出bad_cast異常。
----執行期型別辨識(RTTI)過程中,如果交給typeid的參數爲零或空指針,typeid操作符會拋出bad_typeid異常。
----如果發生非預期的異常,bad_exception異常會接手處理,方式如下:當函數拋出異常規格(excepiton specification)以外的異常,bad_excepiton就會調用unexcepted()。例如:
class E1;
class E2;//not derived from E1
void f() throw(E1)//throws only excepiton of type E1
{
...
throw E1();//throws eception of type E1
...
throw E2();//calls unexcepted(),which calls
terminate()
}
f()之中拋出“型別爲E2”的異常,這種動作違反了異常規則(exception specification)的設定,於是喚起了unexcepted(),後者通常喚起了terminate()終止程序。
然而如果在你的異常規則中列出bad_exception,那麼unexcepted()縱使會重新拋出(rethrow)bad_exception異常。
class E1;
class E2;//not derived from E1
void f() throw(E1,std::bad_exception)//throws only excepiton of type E1 or
//bad_exception for any other exception type
{
...
throw E1();//throws eception of type E1
...
throw E2();//calls unexcepted(),which calls
bad_exception
}
1.1.2 C++標準程序庫所發生的異常
C++標準程序庫異常縱使派生自logic_error。從理論上講,我們能夠通過一些手段,從程序中避免邏輯錯誤。所謂邏輯錯誤包括違背邏輯前提或者違反class的不變性。C++ STL提供以下邏輯錯誤類別:
1)invalid_argument表示無效參數,例如講bitset(array of bits)以char而非'0'或'1'進行初始化。
2)length_error指出某個行爲"可能超越了最大極限",例如對着某個字符串附加太多字符。
3)out_range 指出參數值“不在預期範圍之內”,例如在諸如array的容器或字符串(string)中採用一個錯誤索引。
4)domain_error指出專業領域範疇內的錯誤。
此外,標準程序庫的I/O部分提供了一個ios_base::failure的特殊異常。當數據流(data stream)由錯誤或由到達文件尾端而發生狀態改變時,就可能跑出這個異常。
1.1.3 程序作用域(scope of a program)之外發生的異常
派生自runtime_error的異常,用來指出“不在程序範圍內,且不容易迴避“的事件。C++標準程序庫對執行期錯誤提供了一下三個classes:
1)range_error指出內部算術運算髮生區間錯誤(range error)
2)overflow_error指出算術運算髮生上溢位(overflow)
3)underflow_error指出算術運算髮生下溢位(underflow)
【注】基礎類別exception 和bad_exception定義於<exception>。bad_alloc定義於<new>
bad_cast 和bad_typeid定義於 <typeinfo>,ios_base::failure定義於 <ios>,其它異常類別定義於<stdexcept>。
1.2異常類別(exception class)的成員
爲了catch字句中處理異常,必須採用異常所提供的藉口。所有標準異常的接口只含一個成員函數:what(),用以獲取"型別本身以外的附件信息"。它返回一個以null結束的字符串:
namespace std{
class exception{
public:
virtual const char *what() const throw();
...
};
}
what()返回的c-string在其所屬的異常對象被摧毀後,就不再有效了。
標準異常的其它成員,用來處理生成、複製、銷燬等動作。要注意的是,除了what()外,再沒有任何異常提供任何其它成員函數,能夠描述異常的種類。例如:沒有可找出異常上下文(context)的一致性方法,或找出區間錯誤(range error)發生時的錯誤索引值。因此,唯一通過用的異常評估手段,大概只有打印一途了:
try{
...
}
catch(const std::exception& error){
//print implementation-defined error message
std::cerr<<error.what()<<std::endl;
...
}
1.3拋出標準異常
你可以在自己的程序庫或程序內部拋出某些標準異常。允許你這般運用的各個標準異常,生成時都只需要一個string參數,它將被waht()返回的描述字符串。例如logic_error定義如下:
namespace std{
class logic_error :public exception{
public:
explicit logic_error(const string& whatString);
};
}
提供這種功能的標準異常有:logic_error及其派生類別、runtime_error及其派生類別、ios_base::failure.你不能拋出exception,也不能拋出任何用以支持語言核心性質的異常。
想要拋出一個標準異常,只需生成一個描述符異常的字符串,並將它初始化,交給異常對象;
std::string s;
...
throw std::out_of_range(s);
由char*可被隱式轉換爲string,所以你可以直接使用字符串字面量;
throw std::out_of_range("out_of_range(somewhere,somehow)");
1.4從標準異常類別(exception class)中派生新類別
另一個在程序中採用標準異常類別的可能情況是,定義一個直接或間接派生自exception的特定異常類別。要這麼做,首先必須確保what()機制正常運作,what()是個虛擬函數,所以提供what()方法之一就是自己實現what();
namespace std MyLib{
/*user-defined exception class,
*derived from a standard class for exceptions
*/
class MyProblem : public std::exception{
public:
...
MyProblem(...);{//special constructor
}
virtual const char * what() const throw(){//what() function
...
}
};
...
void f(){
...
//create an exception object and throw it
throw MyProblem(...);
}
}
參考:The C++ Standard Library 侯捷 孟巖 譯