12

If I do

struct MyStruct { ~MyStruct() { } };

void *buffer = operator new(1024);
MyStruct *p = new(buffer) MyStruct();
// ...
delete p;     //    <---------- is this okay?

is the delete guaranteed to take care of both calling ~MyStruct() as well as operator delete?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
user541686
  • 205,094
  • 128
  • 528
  • 886
  • Is there a difference in the resulting memory representation between the way you have allocated and constructed the object vs. doing it in one step? If yes, then the delete will probably fail. If no, then the delete will probably succeed. So the question comes down to how the standard specifies the operation of `new`, not `delete`. – Brent Bradburn May 24 '13 at 00:28
  • I tested this with gcc+Valgrind -- and no errors were reported. – Brent Bradburn May 24 '13 at 00:30

3 Answers3

13

delete p is equivalent to

p->~MyStruct();
operator delete(p);

unless MyStruct has an alternative operator delete, so your example should be well-defined with the expected semantics.

[expr.delete]/2 states:

the value of the operand of delete may be ... a pointer to a non-array object created by a previous new-expression.

Placement new is a type of new-expression. [expr.new]/1:

new-expression:
    ::opt new new-placementopt new-type-id new-initializeropt
    ::opt new new-placementopt ( type-id ) new-initializeropt

delete is defined to be a call to the destructor of the object and then a call to the deallocation function for the memory. [expr.delete]/6,7:

... the delete-expression will invoke the destructor (if any) for the object ...

... the delete-expression will call a deallocation function ...

As long as the deallocation function matches the allocation function (which it should, as long as you haven't overloaded operator delete for your class), then this should all be well defined.

Community
  • 1
  • 1
Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • 2
    Hmm. You also have to note that the implementation isn't allowed to add padding between after the return value of `operator new`. (It's not; C++11 §5.3.4/10.) Even so, I still feel uneasy about this. There doesn't seem to be an unavoidable use case since `dynamic_cast()` gets you the most-derived pointer even when you don't know the dynamic type. – Potatoswatter May 24 '13 at 01:21
  • [The uniform consensus is that passing the return value of placement new to `delete` is NOT ok](http://stackoverflow.com/q/4418220/103167) After all, placement new doesn't require that the memory have been gotten from `::operator new()` in the first place. The rest of this answer collapses immediately, with that foundational misunderstanding removed. – Ben Voigt Apr 12 '14 at 00:05
12

[Revised] It's not generally OK, and you can only generally delete something that was obtained with the matching plain new expression. Otherwise it is up to you to guarantee that the deallocation function matches the allocation function (so using ::delete p; would be the safer general solution, assuming your original operator new was in the global namespace). In particular when the class in question (or one of its derived classes) overload operator new you have to be careful.

Since you're using a placement-new expression to create *p, and since there is no such thing as a "placement-delete expression", you have to destroy the object manually and then release the memory:

p->~MyStruct();

operator delete(buffer);
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 3
    Apart from knee-jerk "this is strange and related to memory, so it must be UB", what evidence do you have for this? – Mankarse May 24 '13 at 00:38
  • @Mankarse: `delete p` would potentially invoke `MyStruct::operator delete`, which may do something entirely inappropriate, wheras `::operator delete` is actually called for. – Kerrek SB May 24 '13 at 00:41
  • True, but that doesn't mean that you can **only** `delete` something that was obtained with the plain `new` expression. It can also work in those situations where you know that `MyStruct::operator delete` doesn't exist. (Though I agree that this makes using `delete` somewhat unsafe, and probably makes it a bad idea in most situations). – Mankarse May 24 '13 at 00:47
  • 1
    @Mankarse: Also 5.3.5/2: "the value of the operand of `delete` may be [...] a pointer to a non-array object created by a previous new-expression [...]. If not, the behavior is undefined." – Kerrek SB May 24 '13 at 00:47
  • @Mankarse: "Undefined behaviour" does of course include "does exactly what you think it should do". (So it might work in any one given case, but it's not generally correct.) – Kerrek SB May 24 '13 at 00:48
  • Placement new is a form of `new-expression` (see my answer below), and an object's lifetime only begins when its initialisation is complete (`[basic.life]/1`), so placement new *does* create objects. – Mankarse May 24 '13 at 00:50
  • Hmm, I'm not convinced of either answer yet... @KerrekSB: Could you expand on what would happen if `operator new` and/or `operator delete` were *not* overloaded? Would there be any guarantees then? – user541686 May 24 '13 at 00:55
  • @Mehrdad: Mankarse got it right. If there's no overloaded `operator delete` in the class that's the type of `*p` (the dynamic type if the destructor is virtual), then `delete p;` will look up the function in the global namespace. Assuming that your original `operator new` was also in the global namespace, this should be OK. – Kerrek SB May 24 '13 at 01:00
  • 3
    @Mehrdad: But note that this design isn't safe: By adding something to the class later, someone could remotely break your code, and you'd never know. You can spare yourself all this trouble by just doing The Right Thing. (The *actual* right thing is never to spell out "new" yourself in client code anyway and instead use something like allocators and `allocate_shared` etc.) – Kerrek SB May 24 '13 at 01:02
0

No, you should not generally call delete (in some cases like when operator delete is overleaded it may be ok).

char *buffer = new char[1024];
MyStruct *p = new(buffer) MyStruct(); //placement new "i'm just calling constructor"
p->~MyStruct();  //destroy the object.

//delete p; // WHAT? even if you override operator delete it may be opaque to reader.
delete [] buffer; // THIS DELETE IS OK (if you have first destroyed MyStruct)
milleniumbug
  • 15,379
  • 3
  • 47
  • 71
CoffeDeveloper
  • 7,961
  • 3
  • 35
  • 69