12

I'm dang certain that this code ought to be illegal, as it clearly won't work, but it seems to be allowed by the C++0x FCD.

class X { /* ... */};
void* raw = malloc(sizeof (X));
X* p = new (raw) X(); // according to the standard, the RHS is a placement-new expression
::operator delete(p); // definitely wrong, per litb's answer
delete p; // legal?  I hope not

Maybe one of you language lawyers can explain how the standard forbids this.

There's also an array form:

class X { /* ... */};
void* raw = malloc(sizeof (X));
X* p = new (raw) X[1]; // according to the standard, the RHS is a placement-new expression
::operator delete[](p); // definitely wrong, per litb's answer
delete [] p; // legal?  I hope not

This is the closest question I was able to find.

EDIT: I'm just not buying the argument that the standard's language restricting arguments to function void ::operator delete(void*) apply in any meaningful way to the operand of delete in a delete-expression. At best, the connection between the two is extremely tenuous, and a number of expressions are allowed as operands to delete which are not valid to pass to void ::operator delete(void*). For example:

struct A
{
  virtual ~A() {}
};

struct B1 : virtual A {};

struct B2 : virtual A {};

struct B3 : virtual A {};

struct D : virtual B1, virtual B2, virtual B3 {};

struct E : virtual B3, virtual D {};

int main( void )
{
  B3* p = new E();
  void* raw = malloc(sizeof (D));
  B3* p2 = new (raw) D();

  ::operator delete(p); // definitely UB
  delete p; // definitely legal

  ::operator delete(p2); // definitely UB
  delete p2; // ???

  return 0;
}

I hope this shows that whether a pointer may be passed to void operator delete(void*) has no bearing on whether that same pointer may be used as the operand of delete.

4

4 回答 4

7

[basic.stc.dynamic.deallocation]p3 中的标准规则

否则,提供给operator delete(void*)标准库的值应是先前调用标准库operator new(size_t)operator new(size_t, const std::nothrow_t&)在标准库中返回的值之一,提供给operator delete[](void*)标准库的值应是先前调用返回的值之一或者在标准库中operator new[](size_t)operator new[](size_t, const std::nothrow_t&)

您的delete调用将调用 library' operator delete(void*),除非您已将其覆盖。既然你什么都没说,我会假设你没有。

上面的“应该”实际上应该是“如果不是行为是未定义的”这样的东西,所以它不会被误认为是一个可诊断的规则,它不是由 [lib.res.on.arguments]p1 决定的。这已由 n3225 更正,因此不会再犯错了。

于 2010-12-11T18:59:30.950 回答
3

编译器并不真正关心p来自放置new调用的内容,因此它不会阻止您delete在对象上发出。这样,您的示例可以被认为是“合法的”。

但这行不通,因为delete不能显式调用“放置”运算符。只有在构造函数抛出时才会隐式调用它们,因此析构函数可以运行。

于 2010-12-11T18:39:55.747 回答
2

我想你可能会逃脱它(在特定的编译器上)如果

  1. new/delete是根据malloc/free
  2. 放置new实际上使用与标准相同的机制来跟踪与分配关联的析构函数new,以便调用delete可以找到正确的析构函数。

但它不可能是便携的或安全的。

于 2010-12-11T18:46:48.067 回答
2

我在标准的库部分中发现了这一点,这与位置 (IMO) 一样违反直觉:

C++0x FCD(和 n3225 草案)第 18.6.1.3 节,[new.delete.placement]

这些函数是保留的,C++ 程序可能不会定义取代标准 C++ 库 (17.6.3) 中的版本的函数。(3.7.4) 的规定不适用于 operator new 和 operator delete 的这些保留放置形式。

void*  operator  new(std::size_t  size,  void*  ptr)  throw();
void*  operator  new[](std::size_t  size,  void*  ptr)  throw();
void  operator  delete(void*  ptr,  void*)  throw();
void  operator  delete[](void*  ptr,  void*)  throw();

尽管如此,定义要传递给的合法表达式的部分delete是 5.3.5,而不是 3.7.4。

于 2010-12-11T18:47:34.503 回答