new(buffer)T;
的语句即为 placement new 语法
如何让一个已经 contructed 的 object 重新调用其 constructor?placement new 就是答案。
placement new 可以指定 "new/新建" 的内存的具体位置。
首先补充一点:一般来说,C++ 有四种 storage categories
举一个 auto/thread_local/static 的例子:
struct T {
int val;
T(int v) : val(v) {}
T(int a, int b) : val(a + b) {}
~T() {
cout << "val: " << val << endl;
}
};
{
// static char .. | thread_local char ..
char buffer[16];
T* ptr = new (buffer) T(10);
// 此时 ptr->val == 10, ptr的值 == buffer的首地址
cout << ptr->val;
}
// 离开 scope,对于 auto 类型的 char buffer[10],其内存被回收。
// ptr->~T() 不会被调用。注意是 stack 中的 buffer 数组的析构函数被调用。
// 当然对于 buffer 这样的 POD 并没有析构函数
//
// 如果 buffer 是 thread_local,则当 thread 结束并被join 或者 detach 后,
// buffer 的内存被回收。
//
// 如果 buffer 是 static,则当 process exit 时,process 所有的内存被操作系统回收,
// 当然也包括 buffer。
再举一个例子:
{
// class T constructor T(int);
T t(1);
T* ptr = &t;
// 这里调用它只是为了打印。实际使用中析构函数会释放一些资源,
// 对应构造函数会创建一些资源。
ptr->~T();
// class T constructor T(int, int);
new (ptr) T(2, 3);
}
// t 的生命结束时,t.~T() 再次被调用
再举一个 heap 的例子:
{
// 申请 sizeof(T) 的内存 + 一定数量的 book keeping 内存
T* ptr = new T(1);
// T(1)的内存被直接覆盖了,注意其 destructor 没有被调用
T* ptr2 = new (ptr) T(2, 3);
// ptr->~T() 被调用。一共有:一次底层 malloc,两次 ctor,
// 一次 dtor,一次底层 free 被调用。
// 同时 delete()库函数 会 free book keeping area
delete ptr;
}