在指针上使用delete表达式的合法性不是从通常的new中获得的



我想知道这个代码是否合法:

delete new (operator new(1)) char;

这段代码做了同样的事情,但没有在从placementnew获得的指针上使用delete表达式:

void* p = operator new(1);
new (p) char;
delete static_cast<char*>(p);

〔expr.delete#2〕的标准规则:

在单个对象删除表达式中,删除操作数的值可以是空指针值、由以前的非数组新表达式产生的指针值,或者指向由这种新表达式创建的对象的基类子对象的指针。如果不是,则行为未定义。在数组delete表达式中,delete操作数的值可以是空指针值,也可以是上一个数组new-express产生的指针值。68如果不是,则行为未定义。

标准没有规定new表达式不包括placement new。所以我认为按照标准,第一条代码应该是合法的,但第二条应该是非法的。我的想法正确吗?

为什么标准会这么说?调用运算符new并在其上构造对象的操作与新表达式几乎相同,但在获得的指针上使用delete表达式并不一定合法。

对于分配数组,标准似乎允许这样做:

delete[] new (operator new[](2)) char[2];

但由于未指明的开销,它应该是非法的。

为什么标准允许这样做


编辑:根据[interro.object]/13:

对名为operator new或operator new[]的函数的任何隐式或显式调用都会在返回的存储区域中隐式创建对象,并返回一个指向合适创建对象的指针。

所以我甚至可以写以下代码:

delete static_cast<char*>(operator new(1));

它破坏隐式创建的char对象并释放内存,执行与上面的示例相同的操作。但根据标准,这绝对是非法的,因为没有使用新的表达方式。

为什么标准不允许这样做

delete new (operator new(1)) char;似乎是合法的。就像你说的,这个标准并没有对新的安置做出任何例外。

你的第二个例子也是合法的:

void* p = operator new(1);
new (p) char;
delete static_cast<char*>(p);

让我们一步一步地了解发生了什么。对operator new的调用隐式地创建char对象,并返回指向所述对象1的指针。所以p一开始已经指向一个char对象。placement new表达式在旧对象上构造一个新的char对象,结束旧对象的生存期;根据抽象机的规则,这神奇地改变了p的值,使其指向这个新对象;参见[basic.life]/8。最后,static_cast<char*>(p)返回一个指向同一个char对象的指针。也就是说,此指针的值与放置new返回的指针的值相同。如果第一个例子是有效的,那么这个例子也是有效的。

注意[expr.delete]/2并不意味着为了使delete的操作数有效,必须存在某种有效的";监管链";从CCD_ 12表达式到现在正被删除的值。它只是说";。。。一个指针,它是由以前的非数组新表达式产生的"指针的值才是最重要的。在第二个示例中,p的值与从放置new返回的指针的值相同,这要归功于[basic.life]/8。

关于你的第三个例子,delete[] new (operator new(2)) char[2];,我同意它不应该是合法的。我建议提交一份缺陷报告。

1参见[interro.object]/13。

最新更新