如何正确释放placement new分配的内存?
我读过一些文章,当您使用 placement new 时,您必须手动调用析构函数。
考虑下面的代码:
// Allocate memory ourself
char* pMemory = new char[ sizeof(MyClass)];
// Construct the object ourself
MyClass* pMyClass = new( pMemory ) MyClass();
// The destruction of object is our duty.
pMyClass->~MyClass();
据我所知,operator delete
通常会调用析构函数,然后释放内存,对吗?那么我们为什么不使用 delete
来代替呢?
delete pMyClass; //what's wrong with that?
在第一种情况下,在我们像这样调用析构函数后,我们被迫将 pMyClass 设置为 nullptr:
pMyClass->~MyClass();
pMyClass = nullptr; // is that correct?
但是析构函数没有释放内存,对吧? 那么这会是内存泄漏吗?
我很困惑,你能解释一下吗?
I've been reading somewere that when you use placement new then you have to call the destructor manually.
Consider the folowing code:
// Allocate memory ourself
char* pMemory = new char[ sizeof(MyClass)];
// Construct the object ourself
MyClass* pMyClass = new( pMemory ) MyClass();
// The destruction of object is our duty.
pMyClass->~MyClass();
As far as I know operator delete
normally calls the destructor and then deallocates the memory, right? So why don't we use delete
instead?
delete pMyClass; //what's wrong with that?
in the first case we are forced to set pMyClass to nullptr
after we call destructor like this:
pMyClass->~MyClass();
pMyClass = nullptr; // is that correct?
BUT the destructor did NOT deallocate memory, right?
So would that be a memory leak?
I'm confused, can you explain that?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用 new 表达式可以做两件事,它调用分配内存的函数operator new,然后使用placement new 在该内存中创建对象。
delete
表达式调用对象的析构函数,然后调用operator delete
。是的,名字很混乱。由于在您的情况下,您手动使用了放置新,因此您还需要手动调用析构函数。由于您手动分配了内存,因此需要手动释放它。
但是,placement new 也被设计为与内部缓冲区(以及其他场景)一起使用,其中缓冲区未使用
operator new
分配,这就是为什么您不应该这样做对它们调用operator delete
。buffer_struct 类的目的是以任何方式创建和销毁存储,而 main 则负责 MyClass 的构造/销毁,请注意两者是如何(几乎*)完全分开的。
*我们必须确保存储空间足够大
Using the
new
expression does two things, it calls the functionoperator new
which allocates memory, and then it uses placement new, to create the object in that memory. Thedelete
expression calls the object's destructor, and then callsoperator delete
. Yeah, the names are confusing.Since in your case, you used placement new manually, you also need to call the destructor manually. Since you allocated the memory manually, you need to release it manually.
However, placement new is designed to work with internal buffers as well (and other scenarios), where the buffers were not allocated with
operator new
, which is why you shouldn't calloperator delete
on them.The purpose of the
buffer_struct
class is to create and destroy the storage in whatever way, whilemain
takes care of the construction/destruction ofMyClass
, note how the two are (almost*) completely separate from each other.*we have to be sure the storage has to be big enough
这是错误的原因之一是:
您必须使用
delete[]
删除pMemory
,因为它是一个数组:您不能同时执行上述两项操作。
同样,您可能会问为什么不能使用
malloc()
分配内存,放置 new 来构造对象,然后使用delete
删除并释放内存。原因是您必须匹配malloc()
和free()
,而不是malloc()
和delete
。在现实世界中,放置新的和显式的析构函数调用几乎从未被使用过。它们可能由标准库实现内部使用(或用于注释中指出的其他系统级编程),但普通程序员不使用它们。在多年的 C++ 开发生涯中,我从未在生产代码中使用过这样的技巧。
One reason this is wrong:
is that you must delete
pMemory
withdelete[]
since it is an array:You can't do both of the above.
Similarly, you might ask why you can't use
malloc()
to allocate memory, placement new to construct an object, and thendelete
to delete and free the memory. The reason is that you must matchmalloc()
andfree()
, notmalloc()
anddelete
.In the real world, placement new and explicit destructor calls are almost never used. They might be used internally by the Standard Library implementation (or for other systems-level programming as noted in the comments), but normal programmers don't use them. I have never used such tricks for production code in many years of doing C++.
您需要区分
delete
运算符和operator delete
。特别是,如果您使用placement new,则显式调用析构函数,然后调用operator delete
(而不是delete
操作符)来释放内存,即请注意这使用
operator delete
,它比delete
运算符级别更低,并且不担心析构函数(它本质上有点像free
) 。将此与删除运算符进行比较,后者在内部执行的操作相当于调用析构函数并调用删除运算符。值得注意的是,您不必使用
::operator new
和::operator delete
来分配和释放缓冲区 - 就放置 new 而言,它缓冲区如何产生/被破坏并不重要。要点是将内存分配和对象生命周期的关注点分开。顺便说一句,这种情况的一个可能的应用是在游戏中,您可能希望预先分配一大块内存,以便仔细管理内存使用情况。然后,您可以在已经获得的内存中构造对象。
另一种可能的用途是优化的小型、固定大小的对象分配器。
You need to distinguish between the
delete
operator andoperator delete
. In particular, if you're using placement new, you explicitly invoke the destructor and then calloperator delete
(and not thedelete
operator) to release the memory, i.e.Note that this uses
operator delete
, which is lower-level than thedelete
operator and doesn't worry about destructors (it's essentially a bit likefree
). Compare this to thedelete
operator, which internally does the equivalent of invoking the destructor and callingoperator delete
.It's worth noting that you don't have to use
::operator new
and::operator delete
to allocate and deallocate your buffer - as far as placement new is concerned, it doesn't matter how the buffer comes into being / gets destroyed. The main point is to separate the concerns of memory allocation and object lifetime.Incidentally, a possible application of this would be in something like a game, where you might want to allocate a large block of memory up-front in order to carefully manage your memory usage. You'd then construct objects in the memory you've already acquired.
Another possible use would be in an optimized small, fixed-size object allocator.
如果您想象在一个内存块中构造几个 MyClass对象,可能会更容易理解。
在这种情况下,它会像这样:
如果您正在编写编译器或操作系统等,那么您可能会这样做。
在这种情况下,我希望您清楚为什么需要单独的“析构函数”和“删除”步骤,因为您没有理由调用删除。但是,您应该按照通常的方式释放内存(释放、删除、对巨大的静态数组不执行任何操作、如果数组是另一个对象的一部分则正常退出等),并且如果你不这样做,它就会被泄露。
另请注意,正如 Greg 所说,在这种情况下,您不能使用 delete,因为您使用 new[] 分配了数组,因此您需要使用 delete[]。
另请注意,您需要假设您没有覆盖 MyClass 的删除,否则它将执行完全不同的操作,这几乎肯定与“new”不兼容。
所以我认为你不太可能像你所描述的那样调用“删除”,但是它能起作用吗?我认为这基本上与“我有两个不相关的类型没有析构函数。我可以新建一个指向一种类型的指针,然后通过指向另一种类型的指针删除该内存吗?”是同一个问题。换句话说,“我的编译器是否有一个包含所有已分配内容的大列表,或者它可以针对不同类型执行不同的操作”。
恐怕我不确定。阅读规范它说:
我认为这意味着“如果你使用两个不相关的类型,它就不起作用(通过虚拟析构函数多态地删除一个类对象是可以的)”。
所以不,不要这样做。我怀疑它在实践中可能经常起作用,如果编译器确实只查看地址而不是类型(并且这两种类型都不是多重继承类,这会破坏地址),但不要尝试它。
It's probably easier to understand if you imagine constructing several MyClass objects within one block of memory.
In that case, it would go something like:
This is the sort of thing you might do if you're writing a compiler, or an OS, etc.
In this case, I hope it's clear why you need separate "destructor" and "delete" steps, because there's no reason you will call delete. However, you should deallocate the memory however you would normally do it (free, delete, do nothing for a giant static array, exit normally if the array is part of another object, etc, etc), and if you don't it'll be leaked.
Also note as Greg said, in this case, you can't use delete, because you allocated the array with new[] so you'd need to use delete[].
Also note that you need to assume you haven't overridden delete for MyClass, else it will do something totally different which is almost certainly incompatible with "new".
So I think you're unlikley to want to call "delete" as you describe, but could it ever work? I think this is basically the same question as "I have two unrelated types that don't have destructors. Can I new a pointer to one type, then delete that memory through a pointer to another type?" In other words, "does my compiler have one big list of all allocated stuff, or can it do different things for different types".
I'm afraid I'm not sure. Reading the spec it says:
I think that means "If you use two unrelated types, it doesn't work (it's ok to delete a class object polymorphically through a virtual destructor)."
So no, don't do that. I suspect it may often work in practice, if the compiler does look solely at the address and not the type (and neither type is a multiple-inheritance class, which would mangle the address), but don't try it.