C++03 Standard say's:
5.3.5 Delete
[...] In either alternative, if the value of the operand of delete is the null pointer the operation has no effect.[...]
char *p = nullptr;
delete p; //no effect
It means, it is valid to delete null pointer in c++.
What C++17 standard say about calling delete on nullptr pointer?
Yes it is valid, and it results in a noop. reference
If expression evaluates to a null pointer value, no destructors are called, and the deallocation function is not called.
For destructors, [expr.delete]/6:
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.
This technically doesn't say that if the operand is a null pointer value, the destructor isn't invoked. Probably a minor wording issue?
For deallocation, [expr.delete]/7:
If the value of the operand of the delete-expression is a null pointer value, it is unspecified whether a deallocation function will be called as described above.
Unspecified deallocation, but likely no destruction.
Note also, from [basic.stc.dynamic.deallocation]/3, which clarifies that even if the standard library deallocation function is called in this situation, there is no effect:
The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect.
Related
If I use new and get std::bad_alloc compiler do not call constructor because of exception. But how does it works with the nothrow new cause we get pointer in every case? Is there special paragraph in standard for this case?
[expr.new]/16 ... if the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.
Will such code cause a memory leak?
unsigned char *ptr = new unsigned char[2];
ptr++; //pointer now points to the second member of array
delete [] ptr;
According to the standard (e.g. this online draft version), the behaviour of delete-ing a pointer, which has not been obtained previously by new, is undefined:
3.7.4.2 Deallocation functions
(3) If a deallocation function terminates by throwing an exception, the
behavior is undefined. The value of the first argument supplied to a
deallocation function may be a null pointer value; if so, and if the
deallocation function is one supplied in the standard library, the
call has no effect. Otherwise, the behavior is undefined if the value
supplied to operator delete(void*) in the standard library is not one
of the values returned by a previous invocation of either operator
new(std::size_t) or operator new(std::size_t, const std::nothrow_t&)
in the standard library, and the behavior is undefined if the value
supplied to operator delete in the standard library is not
one of the values returned by a previous invocation of either operator
new or operator new[](std::size_t, const
std::nothrow_t&) in the standard library.
The value of ptr+1 is not the one returned by new[], and hence the call to delete[] (ptr+1) is UB. Anything can happen, including that it seems to work correctly; But it is for sure not guaranteed to work correctly.
It is undefined behavior as mentioned here
delete [] expression
expression must be a null pointer value or a pointer value previously
obtained by an array form of new-expression. If expression is anything
else, including if it's a pointer obtained by the non-array form of
new-expression, the behavior is undefined.
This makes sense as delete[] would expect to get some information about number of elements to be deleted from the pointer supplied, which it would not found if a different pointer is passed.
I've written the below code which overloads the new and delete operators and throws an exception in the destructor.
When the exception is thrown, why is the code in the delete operator not executed (and "bye" printed)?
If it shouldn't be executed, (how) is the memory freed? Is one of the other delete operators called? Would overloading one of them instead result in the corresponding code being executed? Or is the memory simply not freed because a failed destruction implies that maybe it shouldn't be?
#include <iostream>
using namespace std;
class A
{
public:
A() { }
~A() noexcept(false) { throw exception(); }
void* operator new (std::size_t count)
{
cout << "hi" << endl;
return ::operator new(count);
}
void operator delete (void* ptr)
{
cout << "bye" << endl;
return ::operator delete(ptr);
}
// using these (with corresponding new's) don't seem to work either
// void operator delete (void* ptr, const std::nothrow_t& tag);
// void operator delete (void* ptr, void* place);
};
int main()
{
A* a = new A();
try
{
delete a;
}
catch(...)
{
cout << "eek" << endl;
}
return 0;
}
Output:
hi
eek
Live demo.
I looked at:
throwing exceptions out of a destructor
How does C++ free the memory when a constructor throws an exception and a custom new is used
And others
But I couldn't find an answer to what exactly happens (1) for an exception in the destructor (as opposed to the constructor) and (2) with an overloaded delete.
I don't need a lecture on throwing an exception in a destructor being bad practice - I just ran into similar code and I'm curious about the behaviour.
I would prefer an answer supported by the standard or similar references, if such references exist.
The
standard Draft N4296 5.3.5, pag 121 says:
[expr.delete] [ 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 ]
So the operator delete has to be called regardeless the destructor throws.
However, as has emerged from the comments, some compilers does not properly call the operator delete. This can be resolved as bug compiler.
Bug tested for:
GCC 4.8
Visual Studio 2015
In the 1998 C++ standard (ISO/IEC 14882 First edition, 1998-09-01) the workings of a delete expression are stated quite simply in "Section 5.3.5 Delete [expr.delete]" in paras 6 and 7.
6 The delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted. In the case of an array, the elements will be destroyed in order of decreasing address (that is, in
reverse order of the completion of their constructor; see 12.6.2).
7 The delete-expression will call a deallocation function (3.7.3.2).
In combination, these clauses require that destructor will be invoked (or destructors for an array) and that the deallocation function will be called unconditionally. There is no provision here for not calling the deallocation function if an exception is thrown.
In the 1998 standard, language lawyers and compiler developers will probably take delight in the sophistry of arguing a different interpretation than I've stated above. Fortunately, things are more explicit in later standards...
In Draft N4296 available from open-std.org the same clauses are expanded as follows: (from memory the wording in the official standard is the same, but I don't have a copy on my current machine)
(emphasis mine)
6 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. In the case of an
array, the elements will be destroyed in order of decreasing address (that is, in reverse order of the completion of their constructor; see 12.6.2).
7 If the value of the operand of the delete-expression is not a null pointer value, then:
(7.1) - If the allocation call for the new-expression for the object to be deleted was not omitted and the allocation was not extended (5.3.4), the
delete-expression shall call a deallocation function (3.7.4.2). The value returned from the allocation call of the new-expression shall be passed as the first argument to the deallocation function.
(7.2) - Otherwise, if the allocation was extended or was provided by extending the allocation of another new-expression, and the delete-expression for every other pointer value produced by a new-expression that had storage provided by the extended new-expression has been evaluated, the
delete-expression shall call a deallocation function. The value returned from the allocation call of the extended new-expression shall be passed as the first argument to the deallocation function.
(7.3) - Otherwise, the delete-expression will not 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]
The note at the end spells out that the deallocation function must be called even if the destructor throws an exception.
I'm unsure offhand which evolution of the standard first spelled things out, but based on the above, the clauses will probably remain in Section 5.3.5 (tag [expr.delete]).
The destructor is called before calling to the delete operator. See cppreference - delete expression
If expression is not a null pointer, the delete expression invokes the destructor (if any) for the object that's being destroyed, or for every element of the array being destroyed (proceeding from the last element to the first element of the array).
After that, unless the matching new-expression was combined with another new-expression (since C++14) the delete expression invokes the deallocation function, either operator delete (for the first version of the expression) or operator delete[] (for the second version of the expression).
Due to this order of operations, the destructor is called and throws an exception before your overloaded version of the delete operator is called.
The default placement new operator is declared in 18.6 [support.dynamic] ¶1 with a non-throwing exception-specification:
void* operator new (std::size_t size, void* ptr) noexcept;
This function does nothing except return ptr; so it is reasonable for it to be noexcept, however according to 5.3.4 [expr.new] ¶15 this means that the compiler must check it doesn't return null before invoking the object's constructor:
-15-
[Note: unless an allocation function is declared with a non-throwing exception-specification (15.4), it indicates failure to allocate storage by throwing a std::bad_alloc exception (Clause 15, 18.6.2.1); it returns a non-null pointer otherwise. If the allocation function is declared with a non-throwing exception-specification, it returns null to indicate failure to allocate storage and a non-null pointer otherwise. —end note] If the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.
It seems to me that (specifically for placement new, not in general) this null check is an unfortunate performance hit, albeit small.
I've been debugging some code where placement new was being used in a very performance-sensitive code path to improve the compiler's code generation and the check for null was observed in the assembly. By providing a class-specific placement new overload that is declared with a throwing exception-specification (even though it can't possibly throw) the conditional branch was removed, which also allowed the compiler to generate smaller code for the surrounding inlined functions. The result of saying the placement new function could throw, even though it couldn't, was measurably better code.
So I've been wondering whether the null check is really required for the placement new case. The only way it can return null is if you pass it null. Although it's possible, and apparently legal, to write:
void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );
I can't see why that would be useful, I suggest it would be better if the programmer had to check for null explicitly before using placement new e.g.
Obj* obj = ptr ? new (ptr) Obj() : nullptr;
Has anyone ever needed placement new to correctly handle the null pointer case? (i.e. without adding an explicit check that ptr is a valid memory location.)
I'm wondering whether it would be reasonable to forbid passing a null pointer to the default placement new function, and if not whether there is some better way to avoid the unnecessary branch, other than trying to tell the compiler the value is not null e.g.
void* ptr = getAddress();
(void) *(Obj*)ptr; // inform the optimiser that dereferencing pointer is valid
Obj* obj = new (ptr) Obj();
Or:
void* ptr = getAddress();
if (!ptr)
__builtin_unreachable(); // same, but not portable
Obj* obj = new (ptr) Obj();
N.B. This question is intentionally tagged micro-optimisation, I am not suggesting that you go around overloading placement new for all your types to "improve" performance. This effect was noticed in a very specific performance-critical case and based on profiling and measurement.
Update: DR 1748 makes it undefined behaviour to use a null pointer with placement new, so compilers are no longer required to do the check.
While I can't see much of a question in there except "Has anyone ever needed placement new to correctly handle the null pointer case?" (I haven't), I think the case is interesting enough to spill some thoughts on the issue.
I consider the standard broken or incomplete wrt the placement new function and requirements to allocation functions in general.
If you look closely at the quoted §5.3.4,13, it implies that every allocation function has to be checked for a returned nullpointer, even if it is not noexcept. Therefore, it should be rewritten to
If the allocation function is declared with a non-throwing exception-specification and returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.
That would not harm the validity of allocation functions throwing exceptions, since they have to obey §3.7.4.1:
[...] If it is successful, it shall return the address of the start of a block of storage whose length in bytes shall be at least as large as the requested size. [...] The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement (3.11) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function).
And §5.3.4,14:
[ Note: when the allocation function returns a value other than null, it must be a pointer to a block of storage in which space for the object has been reserved. The block of storage is assumed to be appropriately aligned and of the requested size. [...] -end note ]
Obviously, a placement new that just returns the given pointer, cannot reasonably check avilable storage size and alignment. Therefore,
§18.6.1.3,1 about placement new says
[...] The provisions of (3.7.4) do not apply to these reserved placement forms of operator new and operator delete.
(I guess they missed to mention §5.3.4,14 at that place.)
However, together these paragraphs say indirectly "if you pass a garbage pointer to the palcement functions, you get UB, because §5.3.4,14 is violated". So it's up to you to check the sanity of any poitner given to placement new.
In that spirit, and with the rewritten §5.3.4,13, the standard could strip the noexcept from placement new, leading to an addition to that indirect conclusion: "...and if you pass null, you get UB as well". On the other hand, its much less likely to have a misaligned pointer or pointer to too few memory than having a null pointer.
However, this would remove the need for checking against null, and it would fit well to the philosophy "don't pay for what you don't need". The allocation function itself would not need to check, because §18.6.1.3,1 explicitly says so.
To round things up, one could consider adding a second overload
void* operator new(std::size_t size, void* ptr, const std::nothrow_t&) noexcept;
Sadly, proposing this to the committee is unlikely to result in a change, because it would break existing code relying on placement new being ok with null pointers.
I ran into my first compiler that changes the lvalue passed to ::delete, but doesn't zero out the lvalue. That is the following is true:
Foo * p = new Foo();
Foo * q = p;
assert(p != 0);
assert(p == q);
::delete p;
assert(p != q);
assert(p != 0);
Note that p is not zero after the delete operation, and it has changed from it's old value. A coworker told me that this is not unusual in his experience having worked with some mainframe C++ compilers that would change p to 0xFFFFFFFF, as well as other compilers that would change p to 0.
Where in the C++ Standard does it say that a compiler is allowed to do this?
Searching through StackOverflow, I found this question: Why doesn’t delete set the pointer to NULL? which had an answer that referred to Bjarne Stroustrup's response that includes the statement:
C++ explicitly allows an implementation of delete to zero out an lvalue operand, and I had hoped that implementations would do that, but that idea doesn't seem to have become popular with implementers.
I've read and re-read section 5.3.5 and 12.5 of the final committee draft C++0x standard, but I'm not seeing the "explicit" part. Am I just looking in the wrong sections of the standard? Or is there a chain of logic that is in the sections but I'm just not connecting together properly.
I don't have my copy of the Annotated C++ Reference Manual anymore. Was it in the ARM that a compiler could do this?
[Edit: Correcting the section reference from 3.5.3 to 5.3.5. I'm also adding an interesting paradox as a counterpoint to Henk's assertion that p is undefined after delete.]
There is an interesting paradox if p is initialized to null.
Foo * p = 0;
Foo * q = p;
assert(p == 0);
assert(p == q);
::delete p;
assert(p == q);
assert(p == 0);
In this case though, the behavior is well documented. When delete gets a null pointer, it is suppose to do nothing, so p remains unchanged.
It might not be so explicit. In 5.3.5/7 it says that the delete expression will call a deallocator function. Then in 3.7.3.2/4 it says that using a pointer that has been deallocated is undefined. Since the value of the pointer cannot be used after the deallocation, then whether the pointer keeps the value or the value is changed by the implementation does not make a difference.
5.3.5/7
The delete-expression will call a deallocation function (3.7.3.2).
3.7.3.2/4
If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value (4.10), the deallocation function shall deallocate the storage referenced by the pointer, render- ing invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined.
The references are from the current standard. In the upcoming standard 5.3.5/7 has been reworded:
C++0x FD 5.3.5/7
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 ]