...
Noncompliant Code Example (function-try-block)
In this noncompliant code example, class SomeClass
destructor attempts to handle exceptions thrown from the destructor of the bad_member
subobject by absorbing them. However, the C++ Standard, [except.handle], paragraph 15 states in part [ISO/IEC 14882-2014], states in part:
The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor.
Consequently, the caught exception will inevitably escape from the SomeClass
destructor. Note that exceptions thrown from noncompliant destructors of class member objects or from base classes cannot be handled because they are implicitly rethrown when control reaches the end of the function-try-block handler, which is the only way to catch such exceptions.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdexcept> class SomeClass { class Bad { bool shouldThrow() const; public: ~Bad() noexcept(false) { if (shouldThrow()) { throw std::logic_error("Something bad"); } } }; Bad bad_member; public: ~SomeClass() try { // ... } catch(...) { // Attempt to handle exceptions thrown from Bad destructor. } } |
Compliant Code Example (try-block)
A destructor should perform the same way whether or not there is an active exception. Typically, this means that it should invoke only operations that do not throw exceptions. If necessary, a try-block may be used if the destructor must invoke an operation that may throw an exception.
Code Block | ||||
---|---|---|---|---|
| ||||
struct SomeClass { ~SomeClass() try { // function-try-block try { // Ordinary try-block // Clean up } catch(...) { // Catch and handle exceptions thrown during cleanup } } catch(...) { // Catch and log exceptions thrown from non-compliantnoncompliant // Destructorsdestructors of member objects or base class subobjects // NOTE: Returning from a destructor function-try-block // causes the caught exception to be implicitly rethrown } }; |
...
The compliant solution does not throw exceptions in the event the deallocation fails , but instead fails as gracefully as possible:
...
CERT C++ Secure Coding Standard | ERR55-CPP. Honor exception specifications ERR50-CPP. Do not call std::terminate(), std::abort(), or std::_Exit() |
MISRA 08 | Rule 15-5-1, "A class destructor shall not exit with an exception" |
Bibliography
[ISO/IEC 14882-2014] | 3.4.7.2, "Deallocation Functions" |
[Meyers 05] | Item 8, "Prevent exceptions from leaving destructors" |
[Sutter 00] | "Never allow exceptions from escaping destructors or from an overloaded operator delete() " (p. 29) |
[Henricson 97] | Recommendation 12.5, "Do not let destructors called during stack unwinding throw exceptions" |
...