Ответ вслух Senior Правило 0/3/5 обновлено

Как написать оператор присваивания

Вопрос

Как правильно реализовать копирующее присваивание для класса, владеющего буфером? Какие проблемы бывают у наивной реализации?

Попробуйте сами

Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.

Показать ответ и разбор

Коротко

Наивный вариант «освободить своё, выделить новое, скопировать» имеет две проблемы. При a = a мы удаляем буфер, который потом читаем как источник.

Что должно прозвучать в ответе

  • Самоприсваивание: удаление своего буфера до копирования

  • Исключение из new оставляет объект в разрушенном состоянии

  • Copy-and-swap: строгая гарантия, одна реализация для copy и move

  • Цена: лишняя аллокация, даже если ёмкости хватает

  • Альтернатива: переиспользовать буфер при достаточной ёмкости, с базовой гарантией

  • Возвращать *this по ссылке

Что могут спросить следом

  • Почему стандартный vector не использует copy-and-swap в operator=?
  • Как реализовать swap, чтобы его находил ADL?

Разбор

Наивный вариант «освободить своё, выделить новое, скопировать» имеет две проблемы. При a = a мы удаляем буфер, который потом читаем как источник. А если new бросит bad_alloc, объект останется с висячим указателем и сломает деструктор. Надёжная классика: copy-and-swap. Оператор принимает аргумент по значению, копия строится до входа в функцию, и если она бросит, наш объект не тронут. Внутри только noexcept-обмен указателей и размеров, а старый буфер уйдёт в деструктор временного объекта. Самоприсваивание тоже работает. Плюс один оператор обслуживает и rvalue: аргумент сконструируется перемещением. Цена в том, что мы всегда выделяем новую память, даже если наш буфер достаточно большой. Поэтому std::vector сам так не делает: он переиспользует ёмкость и даёт базовую гарантию. Для своих классов я по умолчанию беру правило нуля, а если пишу вручную, то copy-and-swap, если производительность присваивания не критична.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.