throw
デストラクタから私たちがいるとどうなりますか?私はそれterminate()
が呼び出されることを知っています、そしてメモリは確かに解放されてデストラクタが呼び出されます、しかし、これはから呼び出される前ですか、それとも後 ですか?おそらくここでの問題は、スタックが巻き戻されている間に使用されることであり、それが問題です。throw
foo
throw
6 に答える
これは、foo から throw が呼び出される前ですか、それとも後ですか?
これが起こっていることです:
foo()
と呼ばれるa
タイプのオブジェクトがA
スタックに作成されます- 次のステートメントはスローします
- ここで、dtor for
a
が呼び出され、別の例外がスローされます std::terminate
が呼び出されます -- これは、例外処理メカニズムを放棄することにほかなりません。
C++0x ドラフトから:
15.5.1 std::terminate() 関数
1次の状況では、微妙なエラー処理テクニックを使用するために、例外処理を放棄する必要があります。
[...] — スタックの巻き戻し (15.2) 中のオブジェクトの破棄が例外を使用して終了したとき、または
2そのような場合、std::terminate() が呼び出されます (18.7.3)。一致するハンドラーが見つからない状況では、std::terminate() が呼び出される前にスタックがアンワインドされるかどうかは実装定義です。他のすべての状況では、 std::terminate() が呼び出される前にスタックが巻き戻されてはなりません。アンワインド プロセスが最終的に std::terminate() の呼び出しを引き起こすという決定に基づいて、実装が途中でスタックのアンワインドを終了することは許可されていません。
注:強調鉱山
g++ では次のようになります。
#include <stdio.h>
class A {
public:
~A()
{
fprintf(stderr, "in ~A\n");
throw "error";
}
};
void foo()
{
A a;
fprintf(stderr, "in foo\n");
throw "error";
}
int main()
{
try {
foo();
}
catch (const char*) {
return 1;
}
return 0;
}
[~/ecc/ellcc/ecc] main% ./a.out
in foo
in ~A
terminate called after throwing an instance of 'char const*'
Abort
[~/ecc/ellcc/ecc] main%
ご覧のとおり、foo でのスローが最初に発生し、次に ~A でのスローによってエラーが発生します。
私が間違っていなければ、一度terminate
呼び出されると、(それ以上) スタックの巻き戻しは発生しません。
terminate
ハンドラ関数を呼び出します (これは で設定できますset_terminate
):
例外処理を終了するときに terminate() によって呼び出されるハンドラー関数の型。
必要な動作: terminate_handler は、呼び出し元に戻らずにプログラムの実行を終了する必要があります。
デフォルトの動作: 実装のデフォルトの terminate_handler は、abort() を呼び出します。
少なくとも、スタックを巻き戻すことができる「呼び出し元に戻らずに実行を終了する」方法を知りません。
例を変更して、期待できることを確認できます。
#include <cstdio>
class A
{
public:
~A() {
puts("Entered A destructor");
throw "error";
}
};
void foo()
{
A a, b;
throw "error";
}
int main()
{
try {
foo();
} catch (const char*) {
return 1;
}
}
2 つの A インスタンスがあり、最初の A のデストラクタが終了するとすぐに実行が終了し、別の例外がエスケープされるため、2 番目のデストラクタは呼び出されません。
あなたが得たものは少し間違っていて、それがあなたがそれを理解していない理由です. ご覧のとおり、デストラクタをスローしてteriminate()
も関数が呼び出されるわけではありません。これは悪い習慣ですが、プログラムの実行にとって致命的ではありません。致命的なのは、まだアクティブな例外がある間に一部のコードがスローされることです。C++ は、新しい例外か古い例外か、さらに伝播する例外を決定できず、両方を伝播することもできません。プログラムの実行にとって致命的と見なされるため、terminate が呼び出されます。
したがって、 throw infoo
がなければ、terminate は呼び出されませんが、 から例外がスローされ~A
ます。したがって、当然のことながら、最初に foo をスローする必要があり、次に 2 回目のスローですべてが壊れます。
オブジェクトa
はスタック オブジェクトであるため、解放する動的メモリはありません。制御が の範囲外になるfoo()
と、スタック フレーム、したがってオブジェクトは存在しなくなります。
説明のために、Microsoft C++ で何が起こるかを次に示します。
#include <iostream>
class A {
public:
~A() {
std::cout << "in ~A" << std::endl;
throw "error";
}
};
void foo() {
A a;
std::cout << "in foo" << std::endl;
throw "error";
}
int main() {
try {
foo();
}
catch (void *) {
std::cout << "exit: 1" << std::endl;
return 1;
}
std::cout << "exit: 0" << std::endl;
return 0;
}
そして結果:
>cpptest1.exe
in foo
in ~A
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
>