抛出异常时,析构函数未执行(无堆栈展开)

时间:2014-01-25 03:56:27

标签: c++ exception destructor managed stack-unwinding

我发现了一种我从未见过的非常奇怪的行为。 我正在开发一个复杂的VS2005 C ++项目。

class Tester
{
public:
    Tester()
    {
        TRACE("Construct Tester");
    }
    ~Tester()
    {
        TRACE("~Destruct Tester");
    }
};

void Thrower()
{
    Tester X;
    throw std::exception("Booom");
}

调用Thrower()时,您希望在跟踪输出中看到什么? 构建测试器然后在堆栈展开时被破坏吗?

至少我希望如此,但Tester的析构函数从未被调用过!

不可能!?!?!?!

这是Visual Studio中的错误吗?

我搜索了很多但是甚至没有在Stackoverflow上我找到了答案。

1 个答案:

答案 0 :(得分:1)

我花了一整天的时间来找出问题所在。

现在我必须更深入地解释一下我在做什么。 我有一个编译成LIB文件的C ++代码。上面的代码(Tester和Thrower)位于这个普通的C ++ LIB文件中。

我还有另一个C ++代码,它编译成一个与这个LIB文件链接的托管C ++ DLL。所以最后两个代码都在同一个DLL中。我有托管函数调用LIB文件中的代码,如下所示:

ManagedWrapper()
{
    try
    {
        Thrower();
    }
    catch (std::exception& e)
    {
        throw new System::Exception(e.what());
    }
}

NOT 工作。 使用此代码,我有内存泄漏和未关闭的网络套接字。 Thrower中的堆栈没有解开。

原因是在达到catch之前不会发生堆栈展开。但是当catch位于另一个库而不是抛出时,堆栈并未解开。 DLL不知道如何在LIB文件中展开堆栈(尽管两者最终都被编译到同一个DLL中!)

但我找到了一个非常简单的解决方案。

在LIB文件中,我必须在ManagedWrapper()和Thrower()之间添加一个中间函数。代码看起来很愚蠢,但它解决了这个问题:

Catcher()
{
    try
    {
         Thrower();
    }
    catch(...) // unwind HERE
    {
        throw;
    }
}

重要的是,此捕获器必须位于引发异常的LIB文件中。当捕获到异常时,堆栈被展开,然后异常被重新抛出到托管包装器。

有时看起来很愚蠢的代码非常聪明!

摘要:永远不要忘记必须始终在引发它们的同一个库中捕获该异常。如果它们跨越图书馆边界,则会出现严重问题。