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



         

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


c::c( )

{

   if( some_error() )

      throw error(this); // ДЕФЕКТ: pi не

инициализирован.

   // ...

   pi = new int[128];    // ДЕФЕКТ: pi не

инициализирован,

                         // если оператор new возбуждает

                         // исключение.

   // ...

   if( some_other_error() )

   {

      delete [] pi;      // Не забудьте сделать это.

      throw error(this); // Это возбуждение безопасно

   }

}

c::~c( )

{

   delete pi;

}

Запомните, что pi

содержит мусор до своей инициализации оператором new. Если возбуждается исключение до вызова new или сам оператор new

возбудит исключение, то тогда pi

никогда не инициализируется. (Вероятно, оно не будет содержать NULL, а будет просто не инициализированно). Когда вызывается деструктор, то оператору delete

передается это неопределенное значение. Решим проблему, инициализировав этот указатель безопасным значением до того, как что-либо испортится:

c::c( ) : pi(NULL) // инициализируется на случай, если оператор

                   // new даст сбой

{

   if( some_error() )

      throw error(this); // Это возбуждение теперь безопасно.

      // ...

   pi = new int[128];    // Сбой оператора new теперь безопасен.

   // ...

   if( some_other_error() )

   {

      delete [] pi;     // Не забудьте высвободить динамическую

                        // память.

      throw error(this); // Это возбуждение безопасно.

   }

}

c::~c( )

{

   if( pi )

      delete pi;

}

Следует помнить, что нужно освобождать успешно выделенную память, если исключение возбуждено после операции выделения, так, как было сделано ранее.

У вас есть возможность почистить предложенный выше код при его использовании с учетом моего совета из предыдущего правила о возбуждении исключения объекта error и скрытия всех сложностей в этом объекте. Однако определение этого класса получается значительно более сложным. Реализация в листинге 16 опирается на тот факт, что деструктор явно объявленного объекта должен вызываться при выходе из try-блока, перед выполнением catch-блока. Деструктор для объекта, полученного при помощи new, не будет вызван до тех пор, пока память не будет передана оператору delete, что происходит в сообщении destroy(), посланном из оператора catch. Следовательно, переменная has_been_destroyed




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