Pedantically, this may not be OK. As per cppref:
If expression is anything else, including if it is a pointer obtained by the array form of new-expression, the behavior is undefined.
Putting that aside, is the following code OK in practice (T
is non-array, and assuming that new
is not replaced)?
auto p = (T*)operator new(sizeof(T));
new(p) T{};
delete p;
It is said that in cppref that
When calling the allocation function, the new-expression passes the number of bytes requested as the first argument, of type
std::size_t
, which is exactlysizeof(T)
for non-array T.
So I guess this is probably OK. However, it is also said that since C++14,
New-expressions are allowed to elide or combine allocations made through replaceable allocation functions. In case of elision, the storage may be provided by the compiler without making the call to an allocation function (this also permits optimizing out unused new-expression). In case of combining, the allocation made by a new-expression E1 may be extended to provide additional storage for another new-expression E2 if all of the following is true: [...]
Note that this optimization is only permitted when new-expressions are used, not any other methods to call a replaceable allocation function:
delete[] new int[10];
can be optimized out, butoperator delete(operator new(10));
cannot.
I'm not quite sure of the implications. So, is this OK in C++14?
Why am I asking this question? (source)
Sometimes, memory allocation and initialization cannot be done in a single step. You have to manually allocate the memory, do something else, and then initialize the object, e.g., to provide strong exception safety. In this case, if the delete expression cannot be used on the resulting pointer, you have to manually uninitialize and deallocate, which is tedious. To make things worse, in case both new expression and the manual method are employed, you have to track which one is used for each object.
Typically, the free store is a carefully managed system of free and allocated blocks, and new and delete do bookkeeping to keep everything in a consistent state. If you delete again, the system is likely to do the same bookkeeping on invalid data, and suddenly the free store is in an inconsistent state.
When delete is used to deallocate memory for a C++ class object, the object's destructor is called before the object's memory is deallocated (if the object has a destructor). If the operand to the delete operator is a modifiable l-value, its value is undefined after the object is deleted.
delete keyword in C++Pointer to object is not destroyed, value or memory block pointed by pointer is destroyed.
However, it is better practice to use a delete or delete[] with every new or new[] operation, even if the memory will be cleaned up by the program termination. Many custom objects will have a destructor that does other logic than just cleaning up the memory. Using delete guarantees the destruction in these cases.
This code has well-defined behavior if you have p = new(p) T{};
, assuming that no custom (de)allocation functions are in play. (It can be easily hardened against such things; doing so is left as an exercise for the reader.)
The rule is that you must give non-array delete
a pointer to an object created by a non-array new
(or a base class (with virtual destructor) subobject of such an object, or a null pointer). And with that change your code does that.
There's no requirement that the non-array new
be of a non-placement form. There can't be, since you'd better be able to delete new(std::nothrow) int
. And that's a placement new-expression too, even though people don't usually mean that when they talk about "placement new".
delete
is defined to result in a call to a deallocation function (ignoring the elision cases, which is irrelevant here because the only allocation function called by the new-expression is not a replaceable global allocation function). If you set things up so that it passes invalid arguments to that deallocation function, then you get undefined behavior from that. But here it passes the right address (and the right size if a sized deallocation function is used), so it's well-defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With