简体   繁体   English

如何正确释放placement new分配的内存?

[英]How to properly free the memory allocated by placement new?

I've been reading somewere that when you use placement new then you have to call the destructor manually.我一直在阅读,当您使用placement new时,您必须手动调用析构函数。

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?据我所知,运算符delete通常会调用析构函数,然后释放内存,对吗? So why don't we use delete instead?那么为什么我们不使用delete来代替呢?

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 设置为nullptr

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?我很困惑,你能解释一下吗?

Using the new expression does two things, it calls the function operator new which allocates memory, and then it uses placement new, to create the object in that memory.使用new表达式做了两件事,它调用函数operator new来分配内存,然后它使用placement new 在该内存中创建对象。 The delete expression calls the object's destructor, and then calls operator delete . delete表达式调用对象的析构函数,然后调用operator delete Yeah, the names are confusing.是的,名字很混乱。

//normal version                   calls these two functions
MyClass* pMemory = new MyClass;    void* pMemory = operator new(sizeof(MyClass));
                                   MyClass* pMyClass = new( pMemory ) MyClass();
//normal version                   calls these two functions
delete pMemory;                    pMyClass->~MyClass();
                                   operator delete(pMemory);

Since in your case, you used placement new manually, you also need to call the destructor manually.由于在您的情况下,您手动使用了placement new,因此您还需要手动调用析构函数。 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 call operator delete on them.但是,placement new 也设计用于内部缓冲区(和其他场景),其中缓冲区没有分配给operator new ,这就是为什么你不应该在它们上调用operator delete的原因。

#include <type_traits>

struct buffer_struct {
    std::aligned_storage_t<sizeof(MyClass), alignof(MyClass)> buffer;
};
int main() {
    buffer_struct a;
    MyClass* pMyClass = new (&a.buffer) MyClass(); //created inside buffer_struct a
    //stuff
    pMyClass->~MyClass(); //can't use delete, because there's no `new`.
    return 0;
}

The purpose of the buffer_struct class is to create and destroy the storage in whatever way, while main takes care of the construction/destruction of MyClass , note how the two are (almost*) completely separate from each other. buffer_struct类的目的是以任何方式创建和销毁存储,而main负责MyClass的构造/销毁,请注意两者是如何(几乎*)完全分开的。

*we have to be sure the storage has to be big enough *我们必须确保存储空间足够大

One reason this is wrong:这是错误的一个原因:

delete pMyClass;

is that you must delete pMemory with delete[] since it is an array:是你必须用delete[]删除pMemory因为它是一个数组:

delete[] pMemory;

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 then delete to delete and free the memory.同样,你可能会问为什么不能使用malloc()来分配内存,placement new 来构造对象,然后delete来删除和释放内存。 The reason is that you must match malloc() and free() , not malloc() and delete .原因是您必须匹配malloc()free() ,而不是malloc()delete

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++.在做 C++ 的很多年里,我从来没有在生产代码中使用过这样的技巧。

You need to distinguish between the delete operator and operator delete .您需要区分delete运算符和operator delete In particular, if you're using placement new, you explicitly invoke the destructor and then call operator delete (and not the delete operator) to release the memory, ie特别是,如果您使用placement new,则显式调用析构函数,然后调用operator delete (而不是delete运算符)来释放内存,即

X *x = static_cast<X*>(::operator new(sizeof(X)));
new(x) X;
x->~X();
::operator delete(x);

Note that this uses operator delete , which is lower-level than the delete operator and doesn't worry about destructors (it's essentially a bit like free ).请注意,这使用了operator delete ,它比delete运算符低级,并且不担心析构函数(它本质上有点像free )。 Compare this to the delete operator, which internally does the equivalent of invoking the destructor and calling operator delete .将此与delete运算符进行比较,后者在内部相当于调用析构函数并调用operator 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.值得注意的是,您不必使用::operator new::operator delete来分配和解除分配缓冲区 - 就放置 new 而言,缓冲区如何生成/被破坏并不重要。 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.另一种可能的用途是优化的小型、固定大小的对象分配器。

It's probably easier to understand if you imagine constructing several MyClass objects within one block of memory.如果您想象在一块内存中构造多个MyClass 对象,这可能更容易理解。

In that case, it would go something like:在这种情况下,它会是这样的:

  1. Allocate a giant block of memory using new char[10*sizeof(MyClass)] or malloc(10*sizeof(MyClass))使用 new char[10*sizeof(MyClass)] 或 malloc(10*sizeof(MyClass)) 分配一个巨大的内存块
  2. Use placement new to construct ten MyClass objects within that memory.使用placement new 在该内存中构造十个MyClass 对象。
  3. Do something.做一点事。
  4. Call the destructor of each of your objects调用每个对象的析构函数
  5. Deallocate the big block of memory using delete[] or free().使用 delete[] 或 free() 释放大块内存。

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.在这种情况下,我希望清楚为什么需要单独的“析构函数”和“删除”步骤,因为没有理由调用 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[].另请注意,正如 Greg 所说,在这种情况下,您不能使用 delete,因为您使用 new[] 分配了数组,因此您需要使用 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".另请注意,您需要假设您没有覆盖 MyClass 的删除,否则它将做一些完全不同的事情,这几乎肯定与“新”不兼容。

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:阅读它说的规范:

5.3.5 ... If the static type of the operand [of the delete operator] is different from its dynamic type, the static type shall be a base class of the operand's dynamic type and the static type shall have a virtual destructor or the behaviour is undefined. 5.3.5 ...如果[删除运算符]操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,静态类型应具有虚拟析构函数或行为未定义。

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.我怀疑它在实践中可能经常起作用,如果编译器确实只查看地址而不是类型(并且这两种类型都不是多重继承类,这会破坏地址),但不要尝试它。

您将为共享内存 IPC 使用新布局:一个“初始化程序”进程保留并映射共享内存,然后映射的内存由所有进程共享

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM