понедельник, 28 октября 2013 г.

[prog.c++] Пример простого нарушения exception safety

C++ требует очень внимательного отношения к владению ресурсами. Особенно это касается динамически созданных объектов. Особенно в ситуациях, когда могут порождаться исключения (а какой же современный C++ без использования исключений ;)). Вот простой пример ошибки, может возникнуть, когда забываешь про exception safety.

Есть объект, который хранит в себе указатели на динамически созданные объекты. Эти указатели ему передаются посредством метода add. Логика метода add проста: он сохраняет переданный указатель у себя, а уничтожит все полученные таким образом объекты в своем деструкторе. Поскольку логика метода add проста, то и реализация кажется простой:

class some_object_holder_t
{
   std::vector< A * > m_objects;
public :
   ...
   void add( std::unique_ptr< A > object )
   {
      m_objects.push_back( object.release() );
   }

}

Но я вижу в этом простом коде одну проблему: если при работе push_back произойдет исключение (например, не хватит памяти для расширения вектора), то переданный в add() объект не будет уничтожен. Почему? Да потому, что он уже не будет находиться под контролем std::unique_ptr, ведь метод unique_ptr::release() отработает еще до входа в push_back.

Вот и получаем неприятную ошибку на ровном месте. Неприятную потому, что она может очень долго не проявляться (пока при каком-то стечении обстоятельств push_back не бросит исключение). Поэтому написавший такой код разработчик может сам никогда и не столкнуться с ее последствиями.

Если бы метод add был написан вот так:

   void add( std::unique_ptr< A > object )
   {
      m_objects.push_back( object.get() );
      object.release();
   }

То не было бы нарушения exception safety и, как следствие, очень неприятной и редко возникающей ошибки.

Комментариев нет: