Правила программирования на Си и Си++



         

161. Возбуждение исключений из конструктора ненадежно


Я начну этот раздел с замечания о том, что компиляторы, которые соответствуют рабочим документам комитета 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 по Си++ показало, что такое поведение некорректно — память должна освобождаться неявно. Тем не менее, многие компиляторы делают это неправильно.

Вот простой способ исправить эту сложную ситуацию (я поместил тело функции в определение класса лишь для того, чтобы сделать пример покороче):




Содержание  Назад  Вперед