Interested Article - Copy-and-swap

Идиома copy-and-swap — это идиома языка программирования C++ , позволяющая разрабатывать устойчивые к исключениям операторы присваивания.

Идиома базируется на идиоме « Получение ресурса есть инициализация ».

Идиома предполагает реализацию следующих функций-членов класса:

  • конструктора копирования;
  • оператора присваивания;
  • метода swap , не генерирующего исключения и принимающего ссылку на объект класса.

Пример:

class Copyable {
public:
   Copyable& operator=(const Copyable &_v) {
      Copyable tmp(_v);
      this->swap(tmp);
      return *this;
   }

   void swap(Copyable &_v) noexcept;
};

Устойчивость к исключениям заключается в том, что в операторе присваивания Copyable& operator=(const Copyable &) нет точки, где генерация исключения могла бы привести к утечке памяти.

Оператор присваивания сначала пытается захватить ресурс «временная копия присваиваемого объекта» ( tmp ) и в случае успеха меняет его содержимое с содержимым текущего объекта ( this ). Поскольку метод swap объявлен как не генерирующий исключения ( noexcept ), единственной точкой, где может возникнуть исключение, является копирование объекта _v . Если копирование не удается, то управление не доходит до метода swap , в противном случае деструктор объекта tmp освобождает ресурсы, прежде принадлежавшие текущему объекту ( this ) (см. идиому RAII ).

Приведённая выше реализация также устойчива к присваиваниям объекта самому себе ( a=a ), однако содержит издержки, связанные с тем, что временная копия в этом случае тоже будет создаваться. Исключить издержки можно дополнительной проверкой:

class Copyable {
public:
   Copyable& operator=(const Copyable &_v) {
      if(this != &_v)
          Copyable(_v).swap(*this);
      return *this;
   }

   void swap(Copyable &_v) noexcept;
};

Многие контейнеры и алгоритмы стандартной библиотеки C++ и библиотеки STL предполагают наличие устойчивого к исключениям оператора присваивания, но без использования идиомы copy-and-swap иногда довольно сложно реализовать такой оператор присваивания для классов, содержащих, например, указатели на экземпляры других классов.

Другие операции

Имея функцию-член swap , не генерирующую исключений, можно применять подобную технику, чтобы сделать любую операцию над объектом строгой в отношении к гарантиям безопасности исключений ( strong exception-safe guarantee ).

Для этого сначала делают копию существующего объекта, выполняют над копией необходимые модификации, а потом меняют *this и временный объект.

  • если исключение генерирует конструктор копирования, то исходный объект не модифицируется и строгая гарантия безопасности исключений выполняется;
  • если исключение генерируется при изменении временного объекта, то у временного объекта вызовется деструктор и гарантия тоже будет выполнена поскольку исходный объект модифицирован не был;
  • если изменение временного объекта успешно отработало, то срабатывает swap и деструктор временного объекта, не генерирующие исключений.

См. также

Источник —

Same as Copy-and-swap