According to GOTW #56, there is a potential classic memory leak and exception safety issues in the following code:
// In some header file:
void f( T1*, T2* );
// In some implementation file:
f( new T1, new T2 );
The reason is that when we new T1
, or new T2
, there might be exceptions thrown from the classes' constructors.
Meanwhile, according to the explanation:
Brief recap: An expression like "new T1" is called, simply enough, a new-expression. Recall what a new-expression really does (I'll ignore placement and array forms for simplicity, since they're not very relevant here):
it allocates memory
it constructs a new object in that memory
if the construction fails because of an exception the allocated memory is freed
So each new-expression is essentially a series of two function calls: one call to operator new() (either the global one, or one provided by the type of the object being created), and then a call to the constructor.
For Example 1, consider what happens if the compiler decides to generate code as follows:
1: allocate memory for T1
2: construct T1
3: allocate memory for T2
4: construct T2
5: call f()The problem is this: If either step 3 or step 4 fails because of an exception, the C++ standard does not require that the T1 object be destroyed and its memory deallocated. This is a classic memory leak, and clearly not a Good Thing. [...]
By reading more:
Why doesn't the standard just prevent the problem by requiring compilers to Do The Right Thing when it comes to cleanup?
The basic answer is that it wasn't noticed, and even now that it has been noticed it might not be desirable to fix it. The C++ standard allows the compiler some latitude with the order of evaluation of expressions because this allows the compiler to perform optimizations that might not otherwise be possible. To permit this, the expression evaluation rules are specified in a way that is not exception-safe, and so if you want to write exception-safe code you need to know about, and avoid, these cases. (See below for how best to do this.)
So my questions are:
How to fix this typical exception unsafe code? Should we simply avoid writing code like this?
The answer confused me a little bit, in order to handle constructor failures, we should throw exception from constructor according to C++ FAQ and make sure memory allocated are released properly, so assuming that the class T did implement code that handles construction failure, do we still have exception safety issue in the above code?
Thank you for your time and help.