スタック・アンワインド

C++例外がスローされ、 制御が try ブロックからハンドラーに渡されると、C++ ランタイムは、 try ブロックの開始以降に作成されたすべての自動オブジェクトに対して、デストラクターを呼び出します。 この処理は、スタック・アンワインド と呼ばれます。 自動オブジェクトは、その構築の逆順で破棄されます。 (自動オブジェクトは、auto または register と宣言されているか、 あるいは static または extern と宣言されていない、ローカル・オブジェクトです。 自動オブジェクト x は、x が宣言されているブロックのプログラムの終了時に、 必ず削除されます。)

例外が、サブオブジェクトまたは配列エレメントを含むオブジェクトの構築中にスローされる場 合、デストラクターは、例外がスローされる前に正常に構築されたサブオブジェクトまたは配列エ レメント に対してのみ、呼び出されます。 ローカル静的オブジェクトに対するデストラクターは、オブジェクトが正常に構築された場合にのみ呼び出されます。

スタック・アンワインド中にデストラクターが例外をスローし、 その例外が処理されない場合は、terminate() 関数が呼び出されます。 次の例は、このことを示しています。

#include <iostream>
using namespace std;
 
struct E {
  const char* message;
  E(const char* arg) : message(arg) { }
};
 
void my_terminate() {
  cout << "Call to my_terminate" << endl;
};
 
struct A {
  A() { cout << "In constructor of A" << endl; }
  ~A() {
    cout << "In destructor of A" << endl;
    throw E("Exception thrown in ~A()");
  }
};
 
struct B {
  B() { cout << "In constructor of B" << endl; }
  ~B() { cout << "In destructor of B" << endl; }
};
 
int main() {
  set_terminate(my_terminate);
 
  try {
    cout << "In try block" << endl;
    A a;
    B b;
    throw("Exception thrown in try block of main()");
  }
  catch (const char* e) {
    cout << "Exception: " << e << endl;
  }
  catch (...) {
    cout << "Some exception caught in main()" << endl;
  }
 
  cout << "Resume execution of main()" << endl;
}

次に、上記の例の出力を示します。

In try block
In constructor of A
In constructor of B
In destructor of B
In destructor of A
Call to my_terminate

try ブロックでは、2 つの自動オブジェクト、ab が作成されます。 try ブロックは、型 const char* の例外をスローします。 ハンドラー catch (const char* e) が、この例外をキャッチします。 C++ ランタイムは、スタックをアンワインドし、a および b のデストラクターを、それらが 構築された逆順で呼び出します。 a のデストラクターが、例外をスローします。 プログラムに、 この例外を処理できるハンドラーが存在しないので、C++ ランタイムは terminate() を呼び出します。 (関数 terminate() は、set_terminate() に引き数として指定された関数を呼び出します。 この例では、terminate() は、my_terminate() を呼び出すよう指定されています。)

関連参照

IBM Copyright 2003