C++ 为什么 Windows 上的向量删除会调用标量删除析构函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4948339/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
why is a scalar deleting destructor being called as a result of vector delete on Windows?
提问by noplk
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.
我有一个在 Windows 上泄漏的代码。它在许多 unix 平台上运行良好,泄漏只发生在 Windows 上。二进制文件由 exe、1 个 dll 和 2 个静态库组成。exe 链接到 dll 和静态库,而静态库也链接到 dll。当由于某种原因调用标量删除析构函数而不是调用向量删除析构函数时,exe 代码中会发生泄漏。这导致仅删除数组中的第一个对象,而数组的其余部分保留在内存中。
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???
当 func() 完成并调用析构函数时,我看到只有 m_arr 中第一个对象的析构函数被调用。从调试器我看到这是从标量删除析构函数而不是从向量删除析构函数完成的。这就解释了为什么只有第一个对象被销毁。我需要理解的是为什么在使用 delete [] 时调用标量删除析构函数???
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.
我找到了这个线程 -为什么向量删除析构函数被调用作为标量删除的结果?. 我遵循了那里的建议,并确保所有模块都使用 /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).
重要的是要注意,当包含 ClassFromDll 的 dll 是静态库而不是 dll 时,一切正常。仅当静态库更改为 dll 时才开始泄漏。当程序在 Release 模式下泄漏时,它在 Debug 模式下在 delete [] m_arr 时崩溃。崩溃发生在 dbgdel.cpp 第 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.
在 unix 平台上,这个库也是一个共享库,正如预期的那样,在那里调用向量删除析构函数并且没有泄漏。问题可能出在 VC 编译器上吗?或者也许需要更改项目的其他一些设置?我正在使用 VC2003。
Thank you in advance!
先感谢您!
回答by Markus Kull
This is an old Problem in VC++ regarding DLLs and Object-Arrays. The cause is an incorrect compiler optimization as explained by Microsoft.
这是 VC++ 中关于 DLL 和对象数组的老问题。原因是 Microsoft 解释的不正确的编译器优化。
http://support.microsoft.com/kb/121216/en-us
http://support.microsoft.com/kb/121216/en-us
Better use the STL-containers which dont have the problem due to the use of allocator.
最好使用由于使用分配器而没有问题的 STL 容器。
回答by tenfour
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#?.
DLL 中的类非常敏感,编译器没有太多帮助。查看此答案以了解详细信息为什么这是有问题的:如何调用 C++ DLL 的函数,该函数接受来自 C# 的 stringstream 类型的参数?.
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.
简短版本:如果类接口使用任何内联代码,您将遇到与此类似的潜在问题。包含的任何模板化对象(例如std::string
)。
I would guess this is why. It is similar to the problem @Mikael Persson suggests.
我猜这就是原因。它类似于@Mikael Persson 建议的问题。
回答by Mikael Persson
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.
我认为这是在一个堆上分配并在另一个堆上删除的明显情况(请记住,delete[] 必须查询堆中数组中元素的数量,如果堆甚至不包含此指针,它将返回“错误”(不是真的),并假定它只是一个元素并使用标量删除代替)。我认为您在试图将其归结为一个简单的示例代码时丢失了问题。我建议你阅读这篇文章(它很旧,但删除技术仍然非常重要,我自己使用了该技术的变体,它的作用就像一个魅力)。一种现代方法是将删除函数指针附加到处理您的对象的智能指针 (shared_ptr),这样,通过在工厂函数中创建对象的同时分配此删除函数指针,您可以确保删除将在分配给它的同一个堆上调用。
回答by Martin Stone
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 ;-)
一般来说,我建议使用 std::vector<ClassFromDLL> 而不是 ClassFromDLL*。构造它传入size。然后将自动删除。不幸的是,我对 delete[] 没有太多经验,因为我总是让标准库为我做这件事 ;-)
回答by MSalters
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.
查看代码,我假设对象是由默认复制构造函数复制的,这会导致双重删除错误。这是未定义的行为。它可能看起来有效,但由于看似无关的更改而中断 - 例如从 LIB 切换到 DLL。