1

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

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

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

Thrower()当被调用时,您希望在 Trace 输出中看到什么?当堆栈展开时,那个 Tester 被构造然后被破坏,或者不是?

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

不可能的 !?!?!?!

这是 Visual Studio 中的错误吗?

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

4

1 回答 1

1

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

现在我必须更深入地解释一下我在做什么。我有一个编译成 LIB 文件的 C++ 代码。上面的代码(Tester and 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 文件中。当异常被捕获时,堆栈被展开,然后异常被重新抛出到托管包装器。

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

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

于 2014-01-25T03:56:27.083 回答