C++ 合并原始操作员新建、放置新建和标准删除是否合法?

C++ 合并原始操作员新建、放置新建和标准删除是否合法?,c++,memory-management,new-operator,C++,Memory Management,New Operator,伙计们!出于好奇–以下代码可能不合法,是吗 T *p = ::operator new(sizeof(T)); // allocate memory for a T new (p) T; // construct a T into the allocated memory delete p; //delete the object using the standard delete operator 不可以。您只能删除从新建中获得的内容-没有例外。如果没有DeadMG的正确断言,对您的代码进行

伙计们!出于好奇–以下代码可能不合法,是吗

T *p = ::operator new(sizeof(T)); // allocate memory for a T
new (p) T; // construct a T into the allocated memory
delete p; //delete the object using the standard delete operator

不可以。您只能
删除
新建
中获得的内容-没有例外。

如果没有DeadMG的正确断言,对您的代码进行轻微更改是没有问题的:

unsigned char* addr = new unsigned char[sizeof(MySimpleStructure)];
MySimpleStructure* p = new (addr) MySimpleStructure;
delete [] addr;

由于我们正在删除由
new
返回的
addr
,因此这是合法的。当然,在删除
addr
之后,不应该访问
p
(当时它是一个悬空指针)。还请注意,通过放置新的
p
分配给
MySimpleStructure
MySimpleStructure将不会调用其析构函数。

至少在一种情况下,它显然是未定义的:如果您为
T
重载了
运算符new
运算符delete
,然后,这将尝试使用
::operator new
分配内存,但使用
T::operator delete
删除内存。除非您的
T::operator delete
纯粹是
::operator delete
的包装,否则这将导致一个问题

除此之外,我认为它可能是有定义的。该标准非常具体地说明了这样一个事实,即
新表达式
使用分配函数(§5.3.4/10)分配其内存,该函数将是
::operator new
,只要您没有提供
t::operator new

然后,放置新表达式将初始化对象,正如§5.3.4/15中第一个项目符号所述

然后我们进入毁灭的一方。根据$5.3.5/1:“delete expression操作符销毁最派生的对象(1.8)或由新表达式创建的数组。”这要求您使用新表达式来创建对象——您已经使用了新表达式。您使用了新的位置,这是新表达式的可能性之一,并在§5.3.4/1中规定

下一个适用的要求似乎是:“操作数应该有一个指针类型,或者一个类类型有一个到指针类型的转换函数(12.3.2)。”同样,您的表达式也满足这一要求

我将引用更多的要求,没有进一步的评论,除了您的代码似乎满足所有要求(有些限制了delete表达式的实现,而不是可以在其中使用的指针):

  • (§5.3.5/2):“在第一种备选方案(删除对象)中,删除操作数的值应为指向非数组对象的指针或指向表示此类对象基类的子对象(1.8)的指针(第10条)。否则,行为未定义。”

  • (§5.3.5/3):“在第一个备选方案(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,且静态类型应具有虚拟析构函数或行为未定义

  • (§5.3.5/4):“在第一个备选方案(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,并且静态类型应具有虚拟析构函数,或者行为未定义。”

  • (§5.3.5/6):“删除表达式将调用要删除的对象或数组元素的析构函数(如果有)。”

  • (§5.3.5/7):删除表达式将调用解除分配函数(3.7.3.2)

关于
::operator new
T::operator new
的初始警告,我认为您在
删除表达式中使用的指针符合所有要求,因此应该定义行为


说了这么多,我当然希望这纯粹是学术上的兴趣——尽管在我看来,代码确实定义了行为,但这是一个糟糕的想法,即使是最好的想法。

几乎是@Fredoverflow的复制品,尽管有足够的差异,可能值得讨论。在技术上,这是不允许的s-is(您需要对分配进行强制转换,因为
::operator new
返回
void*
),但如果修复了它,这是一个好问题(我也不确定它是否像DeadMG的回答所暗示的那样简单)。有什么意义?如果您想要
T*
请执行
新T;
!您错过了“无例外”这句话“这是DeadMG回答的一部分。@BoP:这怎么算例外?同意。“你只能删除你从新网站获得的内容”。既然你投了反对票,请解释一下答案是如何违反这条规则的。事实上,如果我发布的内容不合法,请给出一个例子,使用placement new和动态分配的内存,这些内存稍后会被释放。@Chad-你自己说没有调用析构函数。这是怎么回事?调用了addr的“析构函数”,并且给出了上面的代码片段,这就是我们所期望的。Placement new经常以这种方式使用,例如一个字节对齐的结构,后面紧跟着一些任意长度的二进制数据。
MySimpleStructure::~MySimpleStructure
没有被调用这一事实并不成问题,只要编写它的编码人员理解这一事实。
delete
必须与
new
配对,但在本例中是这样的。新的位置仍然是一个新的表达式。您只提到“删除表达式”。关于
::操作员删除
?由于施工涉及手动分配,然后再进行新的布置,因此更对称的破坏顺序不是
p->~t()(销毁),
删除(p)p(无操作),
::操作员删除(p)(解除分配)?是的,为了与他的分配对称,他当然可以这样做。问题是delete表达式是否需要与之等效。可能除了
delete(p)p,我想是的。不必这样,对吧