为什么标记删除析构函数是因为Windows上的向量删除而被调用的?

时间:2011-02-09 17:31:58

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

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

提前谢谢!

5 个答案:

答案 0 :(得分:4)

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

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

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

答案 1 :(得分:1)

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

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

我猜这就是原因。它类似于@Mikael Persson建议的问题。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

一般来说,我建议使用std :: vector< ClassFromDLL>而不是ClassFromDLL *。构造它传递 size 。删除将自动进行。不幸的是我对delete []没有多少经验,因为我总是让标准库为我做这些; - )

答案 4 :(得分:0)

偷看代码,我假设对象是由默认的复制ctor复制的,这会导致双删除错误。这是未定义的行为。它似乎可行,但由于看似无关的更改而中断 - 例如从LIB切换到DLL。