- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
Interestingly, deleting NULL is fine - just does nothing. That path may still run, if you mistakenly manage to invoke that dtor on a null pointer (which is also ub), but it still won't save you from whatever happens later.
Admin
Little typo just above the code sample: that's a destructor, not a constructor...
Admin
As Anon55 said, "delete NULL" is well defined. This is why many developers follow a pattern of setting pointers to NULL after calling delete so that there is not the UB of calling deleted on something that is effectively a random address.
Admin
Yeah, this is prime UB material all over, saved only by the point that the
delete this;
cannot be called, ever, without invoking UB somewhere. (Curiously, the first thing that it would do if we managed to call it to do something would be to call the destructor, which would instantly descend into an infinite recursion(1), thus saving us from the consequences of a double-delete or a delete of something that's not on the heap.)(1) Unless it's a pure virtual destructor at this level, in which case the first call will be UB because the derived class's vtables have already been removed, so all that's left is the pure one. (The destructor is the only function that must have an implementation if it's declared pure virtual, since the derived classes will call it directly.)
Admin
I remember Microsoft's MFC from the early 2000s where they did exactly this, it wasn't UB in their compiler at the time. They had a function like
GetSafeHWND() { if (this) return m_hwnd; return NULL; }
and also something like
CFrameWindow::Close() { delete this; }
Worked fine, but isn't legal anymore.
Admin
Calling "delete this" OUTSIDE the destructor is maybe frowned upon, but legal. But yes, the GetSafeHwnd() code is another WTF in it's own right...
Admin
Careful about the difference between "It's UB" (which is a parameter of the language standards) and "this compiler does a sane thing in this case" (which is a parameter of the specific compiler).
Addendum 2023-03-21 11:42: And I was referring to the specific thing of calling
delete this;
from the destructor.Admin
Setting a pointer to NULL after deletion is actually bad practice. It can hide memory corruption errors especially in a concurrent context and makes it impossible for tooling to track bad pointers.
Admin
delete NULL
means a possible destructor is not called however what happens afterwards is unspecified. Some implementations don't do anything, but keep in mind there could be a custom deallocation function implementation which very well could throw an exception or logs a memory corruption (or does it weirder stuff if it's used for debugging). TLDR: Never call delete with NULL.Admin
then your custom deallocation function is implemented incorrectly.
from the standard, "If the value of the operand of delete is the null pointer the operation has no effect." And in a slightly later version, it is pointed out that custom deallocation functions can be called with said pointer.
Admin
From the cpprefrence.com which can be used as "The Standard" says :
In all cases, if ptr is a null pointer, the standard library deallocation functions do nothing. If the pointer passed to the standard library deallocation function was not obtained from the corresponding standard library allocation function, the behavior is undefined.
Also for huppenzuppen statement that this is illegal : CFrameWindow::Close() { delete this; }
I see no problems with that. No UB. The delete calls destructor ( in case there is a object, if NULL then nothing) and then frees the the memory . But if you have own custom allocator ( hopefully you do not have when using MFC as it was not designed for such use) then you have UB and all the fun.
Admin
Yeah, as I wrote before, the implementation of destruction functions is not specified by the standard; in other words, it can do whatever it wants including throwing exceptions or resting the micro controller (both real life examples) and ofc tooling can use the method as hooks for memory profiling (so you have to check the documentation of all your tools as well).
Solution as with all undefined features in C++ is pretty simple: Never assume it just works because it does right now in this specific case. The reason why those features are left undefined by the standard is usually that there are already real-life implementation which would break it for good reasons.
Admin
No the standard explicitly states that the behavior with destruction functions and NULLs is UNDEFINED. In other words: It can do whatever it wants, and as I stated before there are examples where I encountered that it actually does. Hence there is the exception in the standard, as is pretty normal for C/C++. So you logic is upside down in this case ;-)
Admin
https://en.cppreference.com/w/cpp/language/delete has this to say on
delete p;
whenp
is NULL / nullptr:So there's substantially less "undefined" than you imply in the case of
delete p;
whenp
is NULL, since the destructors aren't called. A specific non-default deallocation function might treat a NULL as UB, but that's bad programming on the part of whoever wrote the function.