Re: exception from constructor
James Kanze wrote:
In all but a few, very rare cases, of course, exceptions are
preferable to these alternatives.
Of course if the constructor of a class can throw exceptions, some
care has to be taken when using such a class.
Example:
class A
{
int* table;
B b; // constructor can throw
// Disallow copying and assignment here, or whatever
public:
A():
table(new int[100]) // bad!
{}
~A() { delete[] table; }
};
The problem with the above code is that if the constructor of 'b'
throws an exception, the table is leaked.
You might try to avoid the problem with an A constructor like this:
A() try:
table(new int[100])
{}
catch(...)
{
delete[] table;
}
This is not ok, though. AFAIK trying to access a member variable from
a constructor catch block is undefined behavior. A practical case can be
constructed by switching the places of 'table' and 'b' inside A. In this
case 'b' is constructed first, and if it throws, the catch block will
attempt to delete an uninitialized pointer (which might not be null!),
which may cause a segmentation fault.
The proper way of implementing the constructor is:
A():
table(0) // ok
{
table = new int[100];
}
Now if b throws, nothing will have been allocated yet for the table,
so nothing leaks. (This is so even if we switch the order of 'table' and
'b'.)
Another possibility is to make 'table' a managed pointer (eg.
auto_ptr, a std::vector or a smart pointer), in which case it's ok to
initialize it in the initialization list.
The general rule is: Never allocate unmanaged memory in the
initialization list.