如何正确释放新安置分配的内存?

cod*_*ddy 23 c++ 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();
Run Code Online (Sandbox Code Playgroud)

据我所知,运算符delete通常会调用析构函数然后释放内存,对吧?那么我们为什么不用delete呢?

delete pMyClass;  //what's wrong with that?
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,我们被迫将pMyClass设置为nullptr在我们调用析构函数之后:

pMyClass->~MyClass();
pMyClass = nullptr;  // is that correct?
Run Code Online (Sandbox Code Playgroud)

但是析构函数没有释放内存,对吧? 这会是内存泄漏吗?

我很困惑,你能解释一下吗?

Moo*_*uck 38

使用new运算符做两件事,它调用operator new分配内存的函数,然后使用placement new,在该内存中创建对象.该delete运营商调用对象的析构函数,然后调用operator delete.是的,这些名字令人困惑.

//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);
Run Code Online (Sandbox Code Playgroud)

因为在您的情况下,您手动使用了新的放置,您还需要手动调用析构函数.由于您手动分配了内存,因此需要手动释放它.请记住,如果你分配new,必须有一个coressponding delete.总是.

但是,placement new设计用于内部缓冲区(以及其他方案),其中缓冲区分配operator new,这就是您不应该调用operator delete它们的原因.

#include <type_traits>

struct buffer_struct {
    std::aligned_storage<sizeof(MyClass)>::type 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;
}
Run Code Online (Sandbox Code Playgroud)

buffer_struct该类的目的是以任何方式创建和销毁存储,同时main负责构造/破坏MyClass,注意两者(几乎*)如何彼此完全分离.

*我们必须确保存储空间足够大

  • 这是问题的根源:"但是,placement new是为内部缓冲区设计的,它们本身没有使用new分配,这就是为什么你不应该在它们上面调用delete." 你没有释放内存,因为你将来会将你的缓冲区用于同一类型的另一个对象.您调用析构函数以便实例可以清理. (2认同)
  • FTR,C++ 11中有`max_align_t`,这是一个具有最大对齐的类型.还有`std :: aligned_storage`.弗雷德说的话仍然存在.这里没有对齐保证. (2认同)

Gre*_*ill 9

这是错误的一个原因:

delete pMyClass;
Run Code Online (Sandbox Code Playgroud)

是你必须删除pMemory,delete[]因为它是一个数组:

delete[] pMemory;
Run Code Online (Sandbox Code Playgroud)

你不能做到以上两点.

同样,您可能会问为什么不能malloc()用来分配内存,新建用于构造对象,然后delete删除并释放内存.原因是,你必须匹配malloc()free()不,malloc()delete.

在现实世界中,几乎从不使用放置新的和显式的析构函数调用.它们可能在标准库实现内部使用(或者在注释中指出的其他系统级编程),但普通程序员不使用它们.在使用C++的许多年里,我从未在生产代码中使用过这些技巧.

  • @codekiddy:当你说`delete [] x;`时,它会为`x`的每个元素调用析构函数.你的数组是`char`,所以你不为每个`char`做任何事情.你只是*曾经*在那里构建并破坏了一个`MyClass`,但这与`new`或`delete`无关. (4认同)
  • @codekiddy:`delete [] pMemory`确实*不*为你的对象调用析构函数. (2认同)

Stu*_*etz 5

您需要区分运算符deleteoperator delete。特别是,如果您使用放置新,则显式调用析构函数,然后调用operator delete(而不是delete运算符)来释放内存,即

X *x = static_cast<X*>(::operator new(sizeof(X)));
new(x) X;
x->~X();
::operator delete(x);
Run Code Online (Sandbox Code Playgroud)

请注意,这使用了operator delete,它比运算符级别更低delete,并且不用担心析构函数(它本质上有点像free)。将此与delete运算符进行比较,该运算符在内部执行相当于调用析构函数并调用operator delete.

值得注意的是,您不必使用::operator new::operator delete分配和取消分配缓冲区 - 就放置新而言,缓冲区如何产生/被销毁并不重要。要点是将内存分配和对象生命周期的关注点分开。

顺便说一句,这种情况的一个可能的应用是在游戏中,您可能希望预先分配一大块内存,以便仔细管理内存使用情况。然后,您可以在已经获得的内存中构造对象。

另一种可能的用途是优化的小型、固定大小的对象分配器。