placement new and delete
Solution 1:
The correct method is:
buf->~Buffer();
::operator delete(mem);
You can only delete with the delete
operator what you received from the new
operator. If you directly call the operator new
function, you must also directly call the operator delete
function, and must manually call the destructor as well.
Solution 2:
There are two separate notions in C++:
The new/delete operators.
New/Delete expressions.
The operators allocate and deallocate memory. The new
expression constructs objects. The delete
expression sometimes destroys an object and calls the operator.
Why "sometimes"? Because it depends on the expression. The naked, global new
first calls operator-new to allocate memory and then constructs the object; the global delete
calls the destructor and deallocates the memory. But all other overloads of new
and delete
are different:
- An overloaded new expression calls an overloaded new operator to allocate memory and then proceeds to construct the object.
- However, there is no such thing as an overloaded delete expression, in particular there is no "placement-delete": Instead, you have to call the destructor manually.
New/Delete operators still have to be overloaded in matching pairs, because the matching delete operator is called when an object constructor throws an exception. However, there is no automatic way to invoke the destructor for an object that has been allocated with an overloaded new
operator, so you have to do that yourself.
As the first and most basic example, consider the placement-new operator, which is mandated to take the form void * operator new (size_t, void * p) throw() { return p; }
. The matching delete
operator is thus mandated to do nothing: void operator delete (void *, void *) throw() { }
. Usage:
void * p = ::operator new(5); // allocate only!
T * q = new (p) T(); // construct
q->~T(); // deconstruct: YOUR responsibility
// delete (p) q; <-- does not exist!! It would invoke the following line:
::operator delete(p, q); // does nothing!
::operator delete(q); // deallocate
Solution 3:
Assuming there's no such thing as Buffer::operator delete
, the delete buf;
version is correct and will do all the appropriate cleanup. To be a bit safer, you can say ::delete buf;
.
Language-lawyer debate material follows.
5.3.5/1
The delete-expression operator destroys a most derived object (1.8) or array created by a new-expression.
delete-expression:
::
optdelete
cast-expression::
optdelete [ ]
cast-expression
The first alternative is for non-array objects, and the second is for arrays. ...
5.3.5/2
... In the first alternative (delete object), the value of the operand of
delete
may be a null pointer, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (1.8) representing a base class of such an object (Clause 10). If not, the behavior is undefined.
So the pointer must point at an object created by a new-expression, which is defined:
5.3.4/1
new-expression:
::
optnew
new-placementoptnew-type-id new-initializeropt::
optnew
new-placementopt(
type-id)
new-initializeropt
new-placement:
(
expression-list)
So a "placement new" does count as a new-expression. Nothing forbidding a delete-expression there.
Also, it turns out the delete-expression does exactly the right things to clean up the object despite the custom creation.
5.3.5/6-9
If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted. ...
If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will call a deallocation function (3.7.4.2). Otherwise, it is unspecified whether the deallocation function will be called. [Note: The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception. - end note]
When the keyword
delete
in a delete-expression is preceded by the unary::
operator, the global deallocation function is used to deallocate the storage.
So ::delete buf;
is entirely equivalent to:
try {
buf->~Buffer();
} catch(...) {
::operator delete(mem);
throw;
}
::operator delete(mem);