Я начну этот раздел с замечания о том, что компиляторы, которые соответствуют рабочим документам комитета ISO/ANSI по Си++, не имеют большей части из рассматриваемых здесь проблем. Тем не менее, многие компиляторы (один из которых компилятор Microsoft) им не соответствуют.
Ошибки в конструкторах являются действительной проблемой Си++. Так как они не вызываются явно, то и не могут возвратить коды ошибок обычным путем. Задание для конструируемого объекта "неверного" значения в лучшем случае громоздко и иногда невозможно. Возбуждение исключения может быть здесь решением, но при этом нужно учесть множество вопросов. Рассмотрим следующий код:
class c
{
class error {};
int *pi;
public:
c() { throw error(); }
// ...
};
void f( void
)
{
try
{
c *cp = new c; // cp не инициализируется, если не
// выполняется
конструктор
// ...
delete
cp; // эта строка в любом случае не выполнится.
}
catch( c::error &err )
{
printf ("Сбой конструктора\n");
delete cp; // Дефект:
cp теперь содержит мусор
}
}
Проблема состоит в том, что память, выделенная оператором new, никогда не освобождается. То есть, компилятор сначала выделяет память, затем вызывает конструктор, который возбуждает объект error. Затем управление передается прямо из конструктора в catch-блок. Код, которым возвращаемое значение оператора new
присваивается cp, никогда не выполняется — управление просто перескакивает через него. Следовательно, отсутствует возможность освобождения памяти, потому что у вас нет соответствующего указателя. Чтение мной рабочих документов комитета ISO/ANSI по Си++ показало, что такое поведение некорректно — память должна освобождаться неявно. Тем не менее, многие компиляторы делают это неправильно.
Вот простой способ исправить эту сложную ситуацию (я поместил тело функции в определение класса лишь для того, чтобы сделать пример покороче):