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,注意两者(几乎*)如何彼此完全分离.
*我们必须确保存储空间足够大
这是错误的一个原因:
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++的许多年里,我从未在生产代码中使用过这些技巧.
您需要区分运算符delete和operator 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分配和取消分配缓冲区 - 就放置新而言,缓冲区如何产生/被销毁并不重要。要点是将内存分配和对象生命周期的关注点分开。
顺便说一句,这种情况的一个可能的应用是在游戏中,您可能希望预先分配一大块内存,以便仔细管理内存使用情况。然后,您可以在已经获得的内存中构造对象。
另一种可能的用途是优化的小型、固定大小的对象分配器。