我们真的需要放置新表达式吗?

Mag*_*ero 2 c++ placement-new dynamic-memory-allocation object-construction

我试图理解C++ 中的放置新表达式

\n

这个 Stack Overflow 答案指出,这T* p = new T(arg);相当于

\n
void* place = operator new(sizeof(T));  // storage allocation\nT* p = new(place) T(arg);               // object construction\n
Run Code Online (Sandbox Code Playgroud)\n

delete p;相当于

\n
p->~T();             // object destruction\noperator delete(p);  // storage deallocation\n
Run Code Online (Sandbox Code Playgroud)\n

为什么我们需要在对象构造中放置 new 表达式T* p = new(place) T(arg);,\xe2\x80\x99t 与以下等价吗?

\n
T* p = (T*) place;\n*p = T(arg);\n
Run Code Online (Sandbox Code Playgroud)\n

Evg*_*Evg 5

首先要注意的是,这*p = T(arg);是一个作业,而不是一个构造。

现在让我们阅读标准([basic.life]/1):

...类型对象的生命周期T从以下时间开始:

  • T获得具有正确对齐方式和类型大小的存储,并且
  • 其初始化(如果有)已完成(包括空初始化)

对于一般类型T,如果使用放置,初始化可能已经完成new,但事实并非如此。正在做

void* place = operator new(sizeof(T));
T* p = (T*)place;
Run Code Online (Sandbox Code Playgroud)

不开始 的生命周期*p

同一部分内容如下([basic.life]/6):

...在对象的生命周期开始之前,但在分配该对象将占用的存储空间之后...任何表示该对象将...所在的存储位置的地址的指针都可以使用,但只能使用以有限的方式。... 如果出现以下情况,则程序具有未定义的行为: ...

  • 指针用于访问非静态数据成员或调用对象的非静态成员函数,...

operator=是一个非静态成员函数,执行*p = T(arg);,相当于p->operator=(T(arg)),会导致未定义的行为。

一个简单的示例是包含一个指针作为数据成员的类,该指针在构造函数中初始化并在赋值运算符中取消引用。如果没有放置,new则不会调用构造函数,并且该指针不会被初始化(完整示例)。