C++ の例外とスタック アンワインドする
C++例外機構では、throwステートメントからスローされた型を処理できる最初のcatchステートメントに移を制御します。catchステートメントに到達すると、スローとcatchステートメントの間の範囲内にあるすべて自動変数は スタック アンワインドと呼ばれるプロセスで破棄されます。スタック アンワインドでは、実行は次のように移動します:
通常の順次実行によって制御が try ステートメントに到達します。try ブロックの保護されたセクションが実行されます。
保護されたセクションの実行中に例外がスローされない場合、try ブロックに続く catch 句は実行されません。実行は、ステートメントの後 try に関連付けられたブロックに続く catch の最後の句の後ろ。
例外が保護されたセクションのまたは保護されたセクションが直接的または間接的に呼び出すルーチンの実行中にスローされると、例外オブジェクトは throw のオペランドによって作成されたオブジェクトから作成されます。(これは、コピー コンストラクターが含まれる場合があることを意味します)。この時点で、コンパイラは、スローされる例外の型を処理できる catch のハンドラーの例外を処理できる型の高い実行コンテキストの catch の句を検索します。catch のハンドラーは、の順序で後 try ブロック確認されました。適切なハンドラーが見つからない場合、次の動的に囲まれている try ブロックが調べられます。このプロセスは、最も外側を囲んでいる try ブロックがチェックされるまで続行されます。
一致するハンドラーがまだない場合、または例外がアンワインド プロセス中に発生した場合は、ハンドラーがコントロールを取得する前に、定義済みのランタイム関数 terminate が呼び出されます。例外がスローされた後、アンワインドが開始される前に例外が発生した場合は、terminate が呼び出されます。
一致している catch ハンドラーが見つかり、そのハンドラーが値でキャッチする場合、その仮パラメーターは例外オブジェクトをコピーして初期化されます。参照によってキャッチする場合、例外オブジェクトを示すためにパラメーターが初期化されます。正式なパラメーターが初期化されると、スタックをアンワインドするプロセスが開始されます。これは、例外の catch のハンドラーとthrowサイトに関連付けられている try ブロックの先頭破棄時に完全に構築しただし、あるすべてのオブジェクトの自動破棄があります。破棄は、構築の逆順に発生します。catch のハンドラーは、ハンドラーするプログラムの実行後に再開 catch のハンドラーではない最初のステートメントまたは構成体の最後、実装されます。コントロールは switch のステートメントで goto のステートメントまたは case のラベルによってスローされた例外に対してのみ catch のハンドラーを、は入力することはできません。
スタック アンワインドの例
次の例では、例外がスローされたときにスタックをアンワインドする方法を示します。スレッドの実行が C のthrowステートメントから maincatchステートメントにジャンプ、各関数を途中でアンワインド。範囲の外になるように Dummy のオブジェクトが作成され、破棄される順序に注意してください。また、catchステートメントを含む関数が mainを除く完了しないことに注意してください。関数 A に呼び出しから B()には返されず、B に呼び出しから C()に決して戻りません。コメント Dummy のポインターの定義と、対応するステートメントを削除し、プログラムを実行すると、ポインターは削除されません。これは、関数が例外を保証しない時に起こるかを示します。詳細については、「方法: 例外に対してデザインする」を参照してください。catchステートメントをコメントになっている場合、プログラムはハンドルされない例外によってアプリケーションが終了するとどうなるかを確認できます。
#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 FunctionC" << endl;
d.MyName = " C";
throw MyException();
cout << "Exiting FunctionC" << endl;
}
void B(Dummy d, int i)
{
cout << "Entering FunctionB" << endl;
d.MyName = "B";
C(d, i + 1);
cout << "Exiting FunctionB" << endl;
}
void A(Dummy d, int i)
{
cout << "Entering FunctionA" << 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;
char c;
cin >> c;
}
/* Output:
Entering main
Created Dummy: M
Copy created Dummy: M
Entering FunctionA
Copy created Dummy: A
Entering FunctionB
Copy created Dummy: B
Entering FunctionC
Destroyed Dummy: C
Destroyed Dummy: B
Destroyed Dummy: A
Destroyed Dummy: M
Caught an exception of type: class MyException
Exiting main.
*/