【问题标题】:why is a scalar deleting destructor being called as a result of vector delete on Windows?为什么在 Windows 上由于向量删除而调用标量删除析构函数?
【发布时间】:2011-02-09 17:31:57
【问题描述】:

我有一个在 Windows 上泄漏的代码。它在许多 unix 平台上运行良好,并且泄漏仅发生在 Windows 上。 二进制文件由 exe、1 个 dll 和 2 个静态库组成。 exe 链接到 dll 和静态库,而静态库也链接到 dll。当由于某种原因调用标量删除析构函数而不是调用向量删除析构函数时,泄漏发生在 exe 代码中。这导致仅删除数组中的第一个对象,而数组的其余部分保留在内存中。

泄漏的伪代码如下所示:

class MyClassFromExe : public MyBaseClassFromDll {
  public:
    ClassFromDll* m_arr;

    MyClassFromExe(unsigned int size)  
    {
      m_arr = new ClassFromDll[size];
    }

    ~MyClassFromExe() 
    {
      delete [] m_arr;
    }
};

void func()
{
  MyClassFromExe obj(3);
}

当 func() 完成并调用析构函数时,我看到只有 m_arr 中第一个对象的析构函数被调用。从调试器我看到这​​是通过标量删除析构函数而不是向量删除析构函数完成的。这解释了为什么只有第一个对象被销毁。 我需要了解的是,为什么在使用delete []的时候会调用标量删除析构函数???

我找到了这个帖子 - Why is vector deleting destructor being called as a result of a scalar delete?。我遵循了那里的建议,并确保所有模块都使用 /MD 编译。

重要的是要注意,当包含 ClassFromDll 的 dll 是静态库而不是 dll 时,一切正常。仅当静态库更改为 dll 时才开始泄漏。 当程序在 Release 模式下泄漏时,它在 delete [] m_arr 上的 Debug 模式下崩溃。崩溃发生在 dbgdel.cpp 第 52 行 - _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)。

在 unix 平台上,这个库也是一个共享库,并且正如预期的那样,向量删除析构函数在那里被调用并且没有泄漏。问题可能出在VC编译器上吗?或者可能需要更改项目的其他一些设置? 我用的是VC2003。

提前谢谢你!

【问题讨论】:

  • 我会调用那个数组。通常通过向量人的意思是std::vector
  • 当它断言处于调试模式时,如果您在附加调试器后尝试执行“重试”,调用堆栈指向哪里?看起来你这里有堆损坏。
  • 这可能与您的问题无关,但您为什么使用这么旧的编译器版本?
  • @Naveen - 当我尝试重试时,什么都没有发生,一切都只是挂起,直到我停止调试器。
  • @Andrey - 我写了向量删除析构函数,因为据我所知,这是编译器创建的用于删除数组的函数的名称。

标签: c++ vector debugging destructor memory-leaks


【解决方案1】:

这是 VC++ 中关于 DLL 和对象数组的一个老问题。原因是 Microsoft 解释的不正确的编译器优化。

http://support.microsoft.com/kb/121216/en-us

最好使用由于使用分配器而没有问题的STL容器。

【讨论】:

  • 感谢您提供此链接!这证实了这确实是一个编译器错误。
【解决方案2】:

DLL 中的类非常敏感,编译器没有太多帮助。查看此答案以获取详细信息为什么会出现问题:How can I call a function of a C++ DLL that accepts a parameter of type stringstream from C#?

短版:如果类接口使用任何内联代码,您将遇到与此完全相同的潜在问题。包括任何模板化对象(例如std::string)。

我猜这就是原因。这类似于@Mikael Persson 提出的问题。

【讨论】:

    【解决方案3】:

    我认为这是在一个堆上分配并在另一个堆上删除的明显情况(请记住,delete[] 必须查询堆中数组中的元素数量,并且如果堆甚至不包含此指针,它将返回“错误”(不是真的),并假定它只是一个元素并使用标量删除)。我认为您在尝试将其归结为简单的示例代码时迷失了问题。我建议您阅读this article(它很旧,但删除技术仍然非常重要,我自己使用了该技术的变体,它就像一个魅力)。一种现代方法是将删除函数指针附加到处理您的对象的智能指针 (shared_ptr),这样,通过在工厂函数中创建对象的同时分配此删除函数指针,您可以确保删除将在分配它的同一堆上调用。

    【讨论】:

    • 感谢您的回复。我还怀疑这可能是不同堆的 dll 和 exe 的问题,但我根本不知道如何解决这个问题。请注意,新调用和删除调用都是从 exe 进行的。实际代码中没有使用工厂,它与我在这里写的非常相似。任何地方都没有 new 和 delete 运算符重载。所有项目都使用 /MD 编译。任何关于如何在不同堆上进行分配和销毁的建议都会有很大帮助。
    【解决方案4】:

    一般来说,我建议使用 std::vector 而不是 ClassFromDLL*。通过 size 构造它。然后将自动删除。不幸的是,我对 delete[] 没有太多经验,因为我总是让标准库为我做这件事;-)

    【讨论】:

      【解决方案5】:

      查看代码,我假设该对象是由默认复制 ctor 复制的,这会导致双重删除错误。这是未定义的行为。它可能看起来可以工作,但由于看似不相关的更改而中断 - 例如从 LIB 切换到 DLL。

      【讨论】:

        猜你喜欢
        • 2011-03-23
        • 2013-04-27
        • 2012-02-12
        • 2015-01-15
        • 2017-01-10
        • 1970-01-01
        • 1970-01-01
        • 2013-11-16
        • 2012-03-13
        相关资源
        最近更新 更多