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
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 destructor is called before calling to the delete operator. See cppreference - delete 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 standard Draft N4296 5.3.5, pag 121 says:
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:
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.
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)
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]).