web-dev-qa-db-ja.com

デストラクタが例外で呼び出されないのはなぜですか?

このプログラムでA::~A()が呼び出されることを期待していましたが、そうではありません。

_#include <iostream>

struct A {
  ~A() { std::cout << "~A()" << std::endl; }
};

void f() {
  A a;
  throw "spam";
}

int main() { f(); }
_

ただし、最後の行をに変更すると

_int main() try { f(); } catch (...) { throw; }
_

次に、A::~A()isが呼び出されます。

Visual Studio 2005の「Microsoft(R)32ビットC/C++最適化コンパイラバージョン14.00.50727.762 for 80x86」でコンパイルしています。コマンドラインは_cl /EHa my.cpp_です。

コンパイラはいつも通りですか?標準はこの問題について何と言っていますか?

50
Constantin

スタックが巻き戻される前に未処理の例外のterminate()が呼び出されるため、デストラクタは呼び出されていません。

C++仕様の具体的な詳細は私の知識の範囲外ですが、gdbとg ++を使用したデバッグトレースはこれを裏付けているようです。

ドラフト標準 セクション15.3の箇条書き9によると:

 9一致するハンドラーがプログラムに見つからない場合、関数terminate()
(_ except.terminate_)が呼び出されます。 terminal()を呼び出す前にスタックが巻き戻されるかどうか
は実装によって定義されます。
65
lefticus

C++言語仕様には次のように記載されています。tryブロックからthrow式へのパス上に構築された自動オブジェクトのデストラクタを呼び出すプロセスは「スタックアンワインド」と呼ばれます。元のコードにはtryブロックが含まれていません。スタックの巻き戻しが発生しないのはそのためです。

15
Alex Che

申し訳ありませんが、標準のコピーを持っていません。
私は間違いなくこれに対する決定的な答えが欲しいので、標準のコピーを持っている誰かが何が起こっているかについて章と詩を共有したいと思います:

私の理解では、終了はiffとのみ呼ばれます。

  • 例外処理メカニズムは、スローされた例外のハンドラーを見つけることができません。
    以下は、これのより具体的なケースです:
    • スタックの巻き戻し中に、例外がデストラクタをエスケープします。
    • スローされた式、例外はコンストラクターをエスケープします。
    • 例外は、非ローカル静的(つまりグローバル)のコンストラクタ/デストラクタをエスケープします
    • 例外は、atexit()に登録されている関数をエスケープします。
    • Main()をエスケープする例外
  • 現在伝播している例外がないときに、例外を再スローしようとしています。
  • 予期しない例外が(予期しない方法で)例外指定子を使用して関数をエスケープする
2
Martin York

この質問はグーグルで簡単に検索できるので、ここで私の状況を共有します。

あなたの例外がextern "C"境界を越えないことを確認するか、MSVCオプション/ EHを使用してください(Extern C関数(/ EH)でC++例外を有効にする=はい)

2
light_keeper

コンパイラは参照されていないため「a」に関連するコードを生成しないと思いましたが、それでも、デストラクタが実行する必要のあることを行うため、これは正しい動作ではありません。

だから、私はVS2008/vc9(+ SP1)で試してみました、デバッグとリリース、そして例外がスローされた後に〜Aが呼び出され、f()から抜け出します-これは正しい動作です私は正しい。

今、私はVS2005/vc8(+ SP1)で試しましたが、同じ動作です。

確かにブレークポイントを使用しました。コンソールで確認したところ、「〜A」というメッセージも表示されます。多分あなたはどこかでそれを間違えましたか?

2
Klaim

2番目の例では、dtorはtry {}ブロックを離れるときに呼び出されます。

最初の例では、main()関数を終了した後にプログラムがシャットダウンするとdtorが呼び出されます。その時点で、coutはすでに破棄されている可能性があります。

1
James Curran