私はそれをよりよく理解するために C++ で例外処理とデストラクタ例外をいじっていましたが、説明できない奇妙な動作に遭遇しました (そして、ここの誰かが私を助けてくれることを願っています)。
この簡単なコードを書きました。これは例外クラス (Foo) で、破棄されると自分自身をスローします。ここでの意図は、例外がスローされた場所から main() まで伝播するようにすることです。そこで明示的に例外をキャッチし、再スローを停止します。
#include <iostream>
class Foo
{
public:
Foo();
virtual ~Foo();
void stopThrowing() { keepThrowing_ = false; }
private:
bool keepThrowing_;
};
Foo::Foo(): keepThrowing_(true)
{
std::cout << "Foo created: " << this << std::endl;
}
Foo::~Foo()
{
std::cout << "Foo destroyed: " << this << std::endl;
if (keepThrowing_)
{
throw Foo();
}
}
int main()
{
try {
try {
throw Foo();
} catch (const Foo&) {
std::cout << "Foo caught" << std::endl;
}
} catch (Foo& ex) {
std::cout << "Foo caught 2" << std::endl;
ex.stopThrowing();
} catch (...) {
std::cout << "Unknown exception caught 2" << std::endl;
}
std::cout << "Done" << std::endl;
return 0;
}
これを C++ で行うべきではないことはわかっていますが、それはポイントではありません。MSVC での x86 と x64 の例外処理の違いを理解しようとしているのです (次の段落で説明します)。
MSVC を使用して x86 用にこのコードをコンパイルすると (主に 2010 を使用しましたが、2005 と 2012 でもこれを確認しました)、すべて問題なく、期待どおりに動作します。
Foo created: 001AFC1C
Foo caught
Foo destroyed: 001AFC1C
Foo created: 001AF31C
Foo caught 2
Foo destroyed: 001AF31C
Done
MSVC を使用して x64 用にこのコードをコンパイルすると、ひどく失敗します。
Foo created: 000000000047F9B8
Foo caught
Foo destroyed: 000000000047F9B8
Foo created: 000000000047D310
Foo destroyed: 000000000047D310
Foo created: 000000000047C150
Foo destroyed: 000000000047C150
Foo created: 000000000047AF90
Foo destroyed: 000000000047AF90
Foo created: 0000000000479DD0
...
その時点で、スタック オーバーフローに達してクラッシュするまで、Foo オブジェクトの作成と破棄を続けます。
デストラクタをこのスニペットに変更すると (Foo の代わりに int をスロー):
Foo::~Foo()
{
std::cout << "Foo destroyed: " << this << std::endl;
if (keepThrowing_)
{
throw 1;
}
}
次の出力が表示されます。
Foo created: 00000000008EF858
Foo caught
Foo destroyed: 00000000008EF858
そして、プログラムが実行されると、デバッグ アサーションに到達します (std::terminate() が呼び出されます) throw 1;
。
私の質問は、ここで何が起こっているのですか? x64 上の MSVC はこの動作を承認していないように見えますが、x86 では動作するため、正しくないと感じます。x86 と x64 の両方で、MinGW と MinGW-w64 を使用してこのコードをコンパイルしたところ、両方のプログラムが期待どおりに動作しました。これは MSVC のバグですか? 誰かがこの問題を回避する方法を考えられますか、それとも Microsoft が x64 でこれが起こらないようにすることを決めた理由はありますか?
ありがとう。