3

我对 C++ 很陌生,但我想确保在向 Microsoft 报告错误之前我没有做错什么。

这是一些示例代码:

#include <system_error>

using namespace std;

class Test
{
public:
    ~Test()
    {
        throw system_error(5, system_category());
    }
};

Test test;

void testfunc()
{
    throw system_error(5, system_category());
}

void main()
{
    try
    {
        testfunc();
    }
    catch ( const system_error& e)
    {
    }
}

现在,我希望 Windows 会说“运行时已请求程序以意外方式退出”。但是,我得到一个“称为纯虚函数”的错误。通过一点调试,我注意到当静态类析构函数获取std::system_category引用时,::name::message成员是纯虚拟的。但是,当它在 中构造时testfunc(),那些 vtable 指针指向有效函数。

system_error我的问题是,以这种方式构建异常,我做错了什么吗?我有一些基本上在做的代码throw system_error(GetLastError(), system_category());。这恰好在静态析构函数中执行,我得到了一个名为 error 的纯虚函数。

要从 Windows 的GetLastError()函数中抛出异常,我应该以不同的方式构建我的异常,还是这是 msvc11 的 C++ 运行时中的错误?

编辑

我的问题有点混乱。我的实际代码比这个例子更复杂,我实际上没想到我的析构函数之一会抛出。我的析构函数必须调用一个可能抛出的函数。如果我将代码更改为:

~Test()
{
    try
    {
        callSomeFuncThatCouldThrow();
    }
    catch ( … ) { }
}

仍然会得到纯虚函数调用错误。这是因为当 system_error 被构造(在 中callSOmeFuncThatCouldThrow())时,它会尝试使用我给它的::message成员,这会导致错误。system_category

4

2 回答 2

4

看起来像微软的错误。std::error_category是各种未命名类型的抽象基类,其中之一是system_category(). 有一个该类型的对象,所有调用都system_category()返回对该对象的引用。您所看到的看起来该对象在对象的析构函数test运行之前已被销毁。如果您想满足纯粹主义者,请将您的析构函数更改为:

Test::~Test() {
    const std::error_category& cat = std::system_category();
    std::cout << cat.name() << '\n';
}
于 2012-09-28T20:07:09.523 回答
3

这是 Visual C++ 标准库实现中的一个错误。全局错误类别对象是使用类模板的静态数据成员实现的。不幸的是,在单个翻译单元(又名源文件)中,这些数据成员将在翻译单元中的所有其他命名空间范围对象之后初始化,并将在这些对象之前被销毁(因为销毁以初始化的相反顺序发生)。

我的建议是避免在初始化和终止期间调用generic_category(),iostream_category()system_category()(即,在所有静态对象被初始化之前或在静态对象的销毁开始之后)。

如果这不可行,则以下解决方法可能有效。它在我运行的几个简单测试中都有效,但我无法保证它在所有情况下的行为(这不是“官方”解决方法;它只是一个潜在的解决方法)。使用以下内容将 .cpp 文件添加到您的项目中:

// This is a workaround for a bug in the Visual C++ 2012 implementation of the
// global error category objects--generic_category(), iostream_category(), and
// system_category().
#ifdef _MSC_VER

#if _MSC_VER != 1700
#  error Please verify that this fix is still required and is still correct!
#endif

#include <system_error>

// Ensure that static objects in this translation unit get initialized "first":
#pragma warning(suppress: 4073)
#pragma init_seg(lib)

// Explicitly instantiate the global error objects in this translation unit:
template struct std::_Error_objects<int>;

#endif // _MSC_VER

#pragma init_seg(lib)应确保在用户代码中的任何静态对象之前初始化静态数据成员_Error_objects<int>。不要在此源文件中添加任何其他内容:#pragma init_seg适用于翻译单元中的所有对象。 _Error_objects是一个实现细节,并且该实现细节可能随时更改,因此编译器版本检查。

于 2012-11-16T22:54:08.680 回答