我一直在使用 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 对象,直到达到堆栈溢出并崩溃。
如果我将析构函数更改为此片段(抛出一个 int 而不是 Foo):
Foo::~Foo()
{
std::cout << "Foo destroyed: " << this << std::endl;
if (keepThrowing_)
{
throw 1;
}
}
我收到以下输出:
Foo created: 00000000008EF858
Foo caught
Foo destroyed: 00000000008EF858
然后程序到达一个调试断言(std::terminate() 被调用),whenthrow 1;
被执行。
我的问题是:这里发生了什么?看起来 x64 上的 MSVC 不批准这种行为,但感觉不对,因为它确实适用于 x86。我使用 MinGW 和 MinGW-w64 为 x86 和 x64 编译了这段代码,两个程序都按预期工作。这是 MSVC 中的错误吗?谁能想到绕过这个问题的方法,或者为什么微软决定阻止这种情况在 x64 上发生?
谢谢。