Legality of using operator delete on a pointer obtained from placement new

邮差的信 提交于 2019-11-26 14:23:01

问题


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.


回答1:


The Standard rules at [basic.stc.dynamic.deallocation]p3

Otherwise, the value supplied to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(size_t) or operator new(size_t, const std::nothrow_t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](size_t) or operator new[](size_t, const std::nothrow_t&) in the standard library.

Your delete call will call the libraries' operator delete(void*), unless you have overwritten it. Since you haven't said anything about that, I will assume you haven't.

The "shall" above really should be something like "behavior is undefined if not" so it's not mistaken as being a diagnosable rule, which it isn't by [lib.res.on.arguments]p1. This was corrected by n3225 so it can't be mistaken anymore.




回答2:


The compiler doesn't really care that p comes from a placement new call, so it won't prevent you from issuing delete on the object. In that way, your example can be considered "legal".

That won't work though, since "placement delete" operators cannot be called explicitly. They're only called implicitly if the constructor throws, so the destructor can run.




回答3:


I suppose you might get away with it (on a particular compiler) if

  1. new/delete are implemented in terms of malloc/free and
  2. the placement new actually uses the same mechanism for keeping track of the destructor associated with allocations as the standard new, so that the call to delete could find the right destructor.

But there is no way it can be portable or safe.




回答4:


I found this in the library section of the standard, which is about as counter-intuitive a location (IMO) as possible:

C++0x FCD (and n3225 draft) section 18.6.1.3, [new.delete.placement]:

These functions are reserved, a C ++ program may not define functions that displace the versions in the Standard C ++ library (17.6.3). The provisions of (3.7.4) do not apply to these reserved placement forms of operator new and 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();

Still, the section defining legal expressions to pass to delete is 5.3.5, not 3.7.4.



来源:https://stackoverflow.com/questions/4418220/legality-of-using-operator-delete-on-a-pointer-obtained-from-placement-new

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!