【问题标题】:Destructors not executed (no stack unwinding) when exception is thrown抛出异常时不执行析构函数(不展开堆栈)
【发布时间】:2014-02-16 06:10:48
【问题描述】:

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

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

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

当调用Thrower() 时,您希望在跟踪输出中看到什么? 该 Tester 是在堆栈展开时构造然后销毁的?

至少我希望如此,但 Tester 的析构函数永远不会被调用!

不可能!?!?!?!

这是 Visual Studio 中的错误吗?

我搜索了很多,但我什至在 Stackoverflow 上都没有找到答案。

【问题讨论】:

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


    【解决方案1】:

    我花了一整天的时间才发现出了什么问题。

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

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

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

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

    这样做的原因是堆栈展开不会在达到 catch 之前发生。但是当 catch 位于另一个库而不是 throw 时,堆栈并没有展开。 DLL不知道如何展开LIB文件中的堆栈(虽然最终都编译成同一个DLL!!)

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

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

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

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

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

    总结:永远不要忘记,必须始终在引发异常的同一库中捕获异常。如果它们被捕获越过图书馆边界,您就会遇到严重的问题。

    【讨论】:

    • 我想听听语言律师关于 C++ 标准是否允许这样做,我怀疑您所描述的是实现中的错误。
    • 如果它是 Visual Studio 中的一个错误,那么互联网上应该有任何信息。但我什么也找不到。但也许你是对的。
    • 在最新版本的 VS 中试一下代码,看来这个问题已经修复了,这个 msdn 链接说明了为什么它是一个错误:social.msdn.microsoft.com/Forums/vstudio/en-US/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-07
    • 1970-01-01
    • 2016-03-07
    • 1970-01-01
    • 2015-08-26
    • 2016-07-13
    相关资源
    最近更新 更多