棧展開(stack unwinding)的定義
抛出異常時,将暫停目前函數的執行,開始查找比對的
catch
子句。首先檢查 throw
本身是否在 try
塊内部,如果是,檢查與該 try
相關的 catch
子句,看是否可以處理該異常。如果不能處理,就退出目前函數,并且釋放目前函數的記憶體并銷毀局部對象,繼續到上層的調用函數中查找,直到找到一個可以處理該異常的 catch
。這個過程稱為棧展開(stack unwinding)。當處理該異常的 catch
結束之後,緊接着該 catch
之後的點繼續執行。 -
為局部對象調用析構函數
在棧展開的過程中,會釋放局部對象所占用的記憶體并運作類類型局部對象的析構函數。但需要注意的是,如果一個塊通過
動态配置設定記憶體,并且在釋放該資源之前發生異常,該塊因異常而退出,那麼在棧展開期間不會釋放該資源,編譯器不會删除該指針,這樣就會造成記憶體洩露。new
-
析構函數應該從不抛出異常
在為某個異常進行棧展開的時候,析構函數如果又抛出自己的未經處理的另一個異常,将會導緻調用标準庫
函數。通常terminate
函數将調用terminate
函數,導緻程式的非正常退出。是以析構函數應該從不抛出異常。abort
-
異常與構造函數
如果在構造函數對象時發生異常,此時該對象可能隻是被部分構造,要保證能夠适當的撤銷這些已構造的成員。
-
未捕獲的異常将會終止程式
不能不處理異常。如果找不到比對的catch,程式就會調用庫函數
。terminate
例子
#include <string>
#include <iostream>
using namespace std;
class MyException{};
class Dummy {
public:
// 構造函數
Dummy(string s) : MyName(s) { PrintMsg("Created Dummy:"); }
// 拷貝構造
Dummy(const Dummy& other) : MyName(other.MyName){ PrintMsg("Copy created Dummy:"); }
// 析構函數
~Dummy(){ PrintMsg("Destroyed Dummy:"); }
void PrintMsg(string s) { cout << s << MyName << endl; }
string MyName;
int level;
};
void C(Dummy d, int i) {
cout << "Entering Function C" << endl;
d.MyName = " C";
throw MyException();
cout << "Exiting Function C" << endl;
}
void B(Dummy d, int i) {
cout << "Entering Function B" << endl;
d.MyName = " B";
C(d, i + 1);
cout << "Exiting Function B" << endl;
}
void A(Dummy d, int i) {
cout << "Entering Function A" << endl;
d.MyName = " A" ;
// Dummy* pd = new Dummy("new Dummy"); //Not exception safe!!!
B(d, i + 1);
// delete pd;
cout << "Exiting FunctionA" << endl;
}
int main() {
cout << "Entering main" << endl;
try {
Dummy d(" M");
A(d,1);
}
catch (MyException& e) {
cout << "Caught an exception of type: " << typeid(e).name() << endl;
}
cout << "Exiting main." << endl;
return 0;
}
/*
*/
進行編譯,運作,可得到如下結果:
$ g++ stack_unwinding.cpp -o stack_test -std=c++11
$ ./stack_test
Entering main
Created Dummy: M
Copy created Dummy: M
Entering Function A
Copy created Dummy: A
Entering Function B
Copy created Dummy: B
Entering Function C
Destroyed Dummy: C
Destroyed Dummy: B
Destroyed Dummy: A
Destroyed Dummy: M
Caught an exception of type: 11MyException
Exiting main.
程式運作時對應棧的内容如下圖所示:

- 根據建立
對象的順序,在它們超出範圍時将其銷毀。Dummy
- 除了包含 catch 語句的
之外,其他函數均未完成。main
- 函數
絕不會從其對A
的調用傳回,并且B()
B
的調用傳回。C()
reference