C++中為什麼說在構造器或析構器中使用異常處理會可能產生嚴重的問題?
在構造函數中拋出異常並不會有問題,而析構中的異常確實是不被鼓勵的,Effective C++中就有一條「Prevent exceptions from leaving destructors」。樓上的匿名用戶舉了這個例子
class test{
public:
int *a;
test(){
a = new int[10];
throw Exception();
}
~test(){
delete[] a;
}
}
確實,test的構造函數異常以後會出問題,但這並不意味著構造函數不能拋出異常。
test(){
a = new int[10];
try{
throw Exception();
}
catch(...){
delete a;
throw;//拋出異常後,首先釋放內存,然後繼續將異常拋出
}
}
看出來了嗎,構造函數不是不能拋出異常,這只是程序本身考慮的不周全。再舉個例子,假如test()不是構造函數,而是一個普通的函數,那麼
int test(){
a = new int[10];
throw Exception();
delete a;
}
也是一種有錯誤的寫法,難道這就能說明普通函數也不準拋出異常?
而析構函數不能拋出異常的原因很簡單:C++不能同時處理兩個異常。class A(){
...
~A(){throw Exception();}
}
void foo(){
std::vector& v;
...
}
現在當vector被析構時,他會一一調用裡面所有的A的析構函數,假如有vector裡面有兩個A,第一個A析構時拋出異常,那麼是處理異常還是繼續析構呢?繼續析構,就會同時出現兩個異常,這是C++無法處理的,一般會自動調用terminate()終結程序運行。而如果選擇處理異常,那麼第二個A的資源又無法釋放。
所以結論就是析構函數不能拋出異常,析構函數必須在自己處理掉異常。@vczh @Fengyang Gao 高票at不到,垃圾知乎
我想說的是,C++允許同時處理多個異常,我舉一個最簡單的例子
[Wandbox]三へ( へ?? ?)へ ????
而為什麼不允許在stack unwind的時候異常逃出析構函數,純粹是因為如果這個逃出來的異常也允許用本想用來catch之前那個異常的catch塊來catch的話,那語義上太容易令人迷惑了,而且就算允許catch了第二個,那第一個要用什麼樣的語法來繼續catch呢?這說不通
所以這些規定說白了就只有一條規則,一個throw在執行流上必須對應一個unique的try-catch block,否則就炸
構造函數是可以拋異常的,只要用 catch 然後 rethrow 的方法,或者構造函數中申請的資源全都支持通過 RAII 釋放就行。
析構函數拋異常的話,如果無法處理,那後面還沒釋放完的資源還要不要繼續釋放?如果後面又遇到異常咋辦?於是只能全部忽略掉,那乾脆就別拋異常了,最多打個 log。新標準析構函數自動noexcept
effective c++里有說。簡單的說,拋exception被catch處理前調用棧會退棧到catch的那一層。然後所有局部變數析構,而這時候有又exception被throw的話,你猜會怎麼樣?
構造函數拋出異常,全局靜態對象的異常怎麼好的捕獲是個問題。
If a deallocation function terminates by throwing an exception, the behavior is undefined.
還有一點得注意,標準中說了,The Currently handled exception is rethrow if control reaches the end of a handler of the function-try-block of a constructor or destructor.
萬全的方法就是一定得防止異常從析構函數中被拋出:一點淺見比如說
class test{
public:
int *a;
test(){
a = new int[10];
throw Exception();
}
~test(){
delete[] a;
}
}
如果這樣調用
try{
test t();
}catch(...){
//do something
}
在test的構造拋出異常以後,不會調用析構函數,所申請的內存也沒有釋放,可能會出問題
推薦閱讀:
※如何理解 struct 的內存對齊?
※C++內存劃分類型?
※在c++中指針是否能夠被完全替代?甚至是不使用指針?
※C++ 是 2012 年不宜進入的技術點嗎?
※c++虛函數表在運行時候是如何存在的?