• Anon55 (unregistered)

    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.

  • Greg (unregistered)

    Little typo just above the code sample: that's a destructor, not a constructor...

  • TheCPUWizard (unregistered)

    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.

  • (nodebb)

    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.)

  • huppenzuppen (unregistered) in reply to Steve_The_Cynic

    delete this; cannot be called, ever, without invoking UB somewhere.

    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.

  • TopTenstion (unregistered)

    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...

  • (nodebb) in reply to huppenzuppen

    it wasn't UB in their compiler at the time.

    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.

  • (nodebb) in reply to TheCPUWizard

    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.

  • (nodebb) in reply to Anon55

    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.

  • NoLand (unregistered)
    Comment held for moderation.
  • asdf (unregistered)
    Comment held for moderation.
  • (nodebb) in reply to MaxiTB

    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.

  • ismo (unregistered) in reply to MaxiTB

    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.

  • (nodebb) in reply to ismo

    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.

  • (nodebb) in reply to thosrtanner

    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 ;-)

  • (nodebb) in reply to MaxiTB

    No the standard explicitly states that the behavior with destruction functions and NULLs is UNDEFINED.

    https://en.cppreference.com/w/cpp/language/delete has this to say on delete p; when p is NULL / nullptr:

    If expression evaluates to a null pointer value, no destructors are called, and the deallocation function may or may not be called (it's unspecified), but the default deallocation functions are guaranteed to do nothing when passed a null pointer.

    So there's substantially less "undefined" than you imply in the case of delete p; when p 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.

Leave a comment on “Magical Destruction”

Log In or post as a guest

Replying to comment #:

« Return to Article