It was said of yore that throwing an exception from a C++ destructor is a verie wycked and wofull thyng.
Remember
goto? Some distinguished gentlemen of the C++ programming trade look down on throwing from destructors as being eviler than using a
goto. Standing base for such moral a judgment is that
"a throwing destructor makes it impossible to write correct code and can shut down your application without any warning" .
Destructors in C++ are one essential ingredient to the
Resource Acquisition Is Initialization (RAII) idiom, which can be resumed as follows: Resources (i.e. memory, sockets, file handles, etc) are acquired during the construction of an object. If an exception is thrown from anywhere in the code during the lifetime and after the object has been fully constructed, the C++ language rules guarantee that the object's destructor will be summoned.
Thus resources can be released gracefully and graciously by said destructor, even in the distasteful and exceptional eventuality of... an exception. The Programmer is relieved from the burdensome duty of having to release resources on every single possible code path.
RAII relieves the programmer from manually preventing leaks. (Notice how nicely
relieves and
leaks go together).
If a destructor, invoked as part of the automatic cleanup that entails the throwing of an exception, decides to throw its own exception, then the system has but two choices: to go into an ambiguous state (now there are two outstanding exceptions, the original one, plus the destructor-thrown one) or... throw the towel.
It is C++ Standard behavior to follow the latter course: the towel is thrown from around the waist, the user is mooned and the program calls
terminate(). Hasta la Vista, Baby. And for this reason, they say your destructors should never throw.
But what if there's not way to recover?
Let's consider a generic Lock object which may look something like this:
template<typename>
class Lock : boost::noncopyable
{
T& mx_;
public:
~Lock() throw()
{
mx_.leave();
}
explicit Lock(T& mx) : mx_(mx)
{
mx_.enter();
}
};
The problem with the code above is that
T::leave() may throw an exception (it may as well not throw, but one really cannot tell, since T is a template parameter).
An so I come to the conclusion of this post. I assert that the code above is just as good as it gets. Of course, T may be bound at compile time to a class that implements the
leave method somewhat like this:
void Mutex::leave()
{
if (int resultCode = pthread_mutex_unlock(&mutex_))
{
throw pthread_runtime_error(resultCode);
}
}
If unlocking the resource fails, then a) something really bad must've happened (possibly a memory corruption?) and b) there is little, if anything, to do about restoring the system to a stable state.
I say let ye programme crash and burne.
What do You reckon?