为什么 Windows 上的向量删除会调用标量删除析构函数?
我有一个在 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 []时会调用标量删除析构函数???
我找到了这个线程 - 为什么标量删除会调用向量删除析构函数?。我遵循了那里的建议,并确保所有模块都使用 /MD 进行编译。
需要注意的是,当包含 ClassFromDll 的 dll 是静态库而不是 dll 时,一切工作正常。仅当静态库更改为 dll 时,泄漏才开始。 虽然程序在发布模式下发生泄漏,但在调试模式下删除 [] m_arr 时会崩溃。崩溃发生在 dbgdel.cpp 第 52 行 - _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)。
在unix平台上,这个库也是一个共享库,并且正如预期的那样,向量删除析构函数被调用,并且没有泄漏。难道是VC编译器的问题?或者项目的其他一些设置可能需要更改? 我用的是VC2003。
先感谢您!
I have a code that is leaking on Windows. It runs fine on many unix platforms and the leak only occurs on Windows.
The binary consists of exe, 1 dll and 2 static libs. The exe links to both the dll and the static libs, while the static libs link with the dll as well. The leak occurs in the exe code when instead of calling to a vector deleting destructor, for some reason scalar deleting destructor is called. This results in only the first object in the array to be deleted while the rest of the array stays in memory.
The leaking pseudo-code looks like this:
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);
}
When func() finishes and the destructor is called I see that only the destructor of the first object in m_arr is called. From debugger I see that this is done from scalar deleting destructor and not from vector deleting destructor. This explains why only the first object is destroyed.
What I need to understand is why scalar deleting destructor is called when delete [] is used???
I found this thread - Why is vector deleting destructor being called as a result of a scalar delete?. I followed the suggestions there and made sure that all the modules are compiled with /MD.
Important to notice that when the dll that contains ClassFromDll was a static library and not a dll, everything worked fine. The leak started only when the static library was changed to be a dll.
While the program leaks in Release mode, it crashes in Debug mode on delete [] m_arr. The crash occurs in dbgdel.cpp line 52 - _BLOCK_TYPE_IS_VALID(pHead->nBlockUse).
On unix platforms this lib is also a shared lib and as expected vector deleting destructor is called there and there is no leak. Could the problem be with the VC compiler? Or maybe some other settings of the projects need to be changed?
I'm using VC2003.
Thank you in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
一般来说,我建议使用 std::vector而不是 ClassFromDLL*。构造它并传入 size。然后将自动删除。不幸的是,我对删除[]没有太多经验,因为我总是让标准库为我做这件事;-)
In general I'd recommend using a std::vector<ClassFromDLL> instead of ClassFromDLL*. Construct it passing in size. Deletion will then be automatic. Unfortunately I don't have much experience with delete[], because I always let the standard library do that for me ;-)
看看代码,我假设该对象是由默认复制构造函数复制的,这会导致双重删除错误。这是未定义的行为。它可能看起来可以工作,但由于看似不相关的更改而中断 - 例如从 LIB 切换到 DLL。
Peeking at the code, I'd assume the object was copied by the default copy ctor, which leads to a double-deletion bug. This is Undefined Behavior. It may appear to work, but break due to seemingly unrelated changes - such as switching from a LIB to a DLL.
DLL 中的类非常敏感,不需要编译器的太多帮助。查看此答案以了解详细信息为什么这是有问题的: 如何调用从 C# 接受 stringstream 类型参数的 C++ DLL 函数?。
简短版本:如果类接口使用任何内联代码,您将遇到与此完全相同的潜在问题。包括任何模板化对象(例如
std::string
)。我猜这就是原因。这与@Mikael Persson 提出的问题类似。
A class in a DLL is extremely touchy, without much help from compilers. Check out this answer for details WHY this is problematic: How can I call a function of a C++ DLL that accepts a parameter of type stringstream from C#?.
Short version: if the class interface uses ANY inlined code, you will experience potential problems exactly like this. Any templated object (such as
std::string
) included.I would guess this is why. It is similar to the problem @Mikael Persson suggests.
我认为这是在一个堆上分配并在另一个堆上删除的明显情况(请记住,delete[] 必须在堆中查询数组中的元素数量,如果堆甚至不包含此指针,它将返回“错误”(不是真的),并且将假定它只是一个元素并使用标量删除代替)。我认为您遇到的问题在尝试将其归结为简单的示例代码时丢失了。我建议你阅读这篇文章(它很旧,但删除技术仍然非常相关,我使用我自己对该技术的变体,它的作用就像一个魅力)。一种现代的方法是将删除函数指针附加到处理对象的智能指针(shared_ptr),这样,通过在工厂函数中创建对象的同时分配此删除函数指针,您可以确保删除将在分配它的同一个堆上调用。
I think this is a clear case of allocating on one heap and deleting on another (remember that the delete[] has to query the heap for the number of elements in the array, and if the heap does not even contain this pointer, it will return "error" (not really) and it will be assumed that it is just one element and use the scalar delete instead). I think that the problem you have was lost in trying to boil it down to a simple example code. I would suggest you read this article (it's old, but the deletion technique is still very relevant, I use a variation of that technique myself and it works like a charm). One modern way to do this is to attach a deletion function pointer to a smart pointer (shared_ptr) that handles your object, this way, by assigning this deletion function pointer at the same time as you create the object in a factory function, you ensure that the delete will be called on the same heap that it was allocated from.
这是 VC++ 中关于 DLL 和对象数组的一个老问题。正如 Microsoft 所解释的,原因是编译器优化不正确。
Microsoft KB Archive/121216
最好使用 STL 容器,因为它不会由于使用分配器而没有问题。
This is an old Problem in VC++ regarding DLLs and Object-Arrays. The cause is an incorrect compiler optimization as explained by Microsoft.
Microsoft KB Archive/121216
Better use the STL-containers which don't have the problem due to the use of allocator.