C++ プログラミングガイド

制御の流れの管理

C++ では、例外ハンドラは例外を訂正してその例外の発生場所に戻ることはしません。その代わり例外が発生すると、制御は例外を送出した関数から抜け、続いて例外を予期していた try ブロックから抜け、その例外と例外宣言が一致する catch ブロックに移ります。

この catch ブロックが例外を処理します。catch ブロックは、同じ例外を再送出するか、別の例外を送出するか、ラベルにジャンプするか、関数から戻るか、あるいは正常に終了します。catch ブロックが throw なしで正常に終了した場合、制御の流れは後続のすべての (try ブロックに関連付けられた) catch ブロックを飛び越えます。

例外が送出および捕獲され、その例外を送出した関数の外に制御が移ると、「スタックの巻き戻し」が実行されます。スタックの巻き戻しを行なっている間、終了したブロックのスコープ内で生成された自動オブジェクトは、そのデストラクタの呼び出しによって安全に破壊されます。

try ブロックが例外なしで終了した場合、関連するすべての catch ブロックは無視されます。


注 -

例外ハンドラは、 return 文を使用してエラーの発生した場所へ制御を戻すことはできません。この場合、発行された return 文はその catch ブロックが入っている関数から戻ります。


try ブロックとハンドラからの分岐

try ブロックやハンドラから外への分岐は許可されています。しかし、catch ブロックの中への分岐は、例外の開始を飛び越すことに等しいので許可されていません。

例外の入れ子

他の例外が処理されていない間に別の例外を送出することを、例外の入れ子と呼びます。例外の入れ子は、特定の状況でしか行えません。例外が送出される位置から一致する catch 節の入力位置までは、例外は処理されません。この間に呼び出される関数 (破壊される自動オブジェクトのデストラクタなど) は、例外が関数を回避しないかぎり、新しい例外を送出できます。他の例外が処理されていない間に例外によって関数が終了すると、その直後に terminate() 関数が呼び出されます。

例外ハンドラがいったん入力されると例外は処理済みと見なされ、例外が再び送出できるようになります。

例外が送出されているが、現在未処理である状態は uncaught_exception() 関数で確認できます。uncaught_exception() 関数の呼び出し」を参照してください。

送出する例外の指定

関数宣言には、例外指定を 1 つ含めることができます。例外指定とは、関数が直接的にまたは間接的に送出する可能性のある例外のことです。

次の 2 つの宣言は、関数 f1 は例外を生成し、その例外は X 型のハンドラが受け取ることおよび、型 WY、または Z のハンドラによって捕獲できる例外だけを関数 f2 が生成することを伝えています。


void f1(int) throw(X);
void f2(int) throw(W,Y,Z);

上記の例を少し変えて書くと、次のようになります。


void f3(int) throw(); // 空の括弧

このように定義すると、関数 f3 は例外を 1 つも生成しなくなります。例外指定で許可されていない例外によって関数が終了する場合、事前に定義済みの関数 unexpected() が呼び出されます。unexpected() は、デフォルトで abort() を呼び出してプログラムを終了します。set_unexpected() 関数を呼び出すと、このデフォルトの動作を変えることができます。terminate()unexpected() 関数の変更」を参照してください。

予期しない例外は、コンパイル時ではなくプログラムの実行時に検査されます。許可されていない例外が送出されそうな場合でも、実行時にその例外が実際に送出されないかぎりエラーは出力されません。

しかしコンパイラは、場合によっては不必要な検査を省くことができます。

たとえば次の例では、f は検査されません。


void foo(int) throw(x);
void f(int) throw(x);
{
     foo(13);
}

例外を指定しておかないと、あらゆる例外が送出される可能性があります。