棧展開(stack unwinding)
阿新 • • 發佈:2021-10-25
棧展開(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.
程式執行時對應棧的內容如下圖所示:
程式執行將從 C
中的 throw 語句跳轉到 main
中的 catch 語句,並在此過程中展開每個函式。
- 根據建立
Dummy
物件的順序,在它們超出範圍時將其銷燬。 - 除了包含 catch 語句的
main
之外,其他函式均未完成。 - 函式
A
絕不會從其對B()
的呼叫返回,並且B
絕不會從其對C()
的呼叫返回。
reference
[1] microsoft C++文件