Семантика копирования и правило пяти
Как определить, что означает копирование, присваивание и уничтожение для ваших собственных типов, владеющих ресурсом
- назвать специальные функции-члены, которые генерирует компилятор, и сказать, что каждая из них делает по умолчанию,
- объяснить разницу между поверхностной и глубокой копией и почему поверхностная копия владеющего класса приводит к двойному освобождению,
- написать корректный конструктор копирования и оператор копирующего присваивания (включая идиому copy-and-swap),
- применять правило трёх, правило пяти и правило нуля и использовать
=defaultи=deleteдля управления тем, какие операции существуют.
Эта тема предполагает, что вы уже знаете сырые указатели, new/delete и RAII. Это недостающее звено, на которое незаметно опирается Введение в семантику перемещения: операции перемещения обретают смысл только после того, как вы поймёте операции копирования.
1. Специальные функции-члены
Есть небольшой набор функций-членов, которые компилятор генерирует за вас, если вы их не написали. Их называют специальными функциями-членами. Для семантики копирования сначала важны три из них:
- Деструктор
~T()- выполняется, когда объект уничтожается; его задача - освободить то, чем объект владеет. - Конструктор копирования
T(const T&)- строит совершенно новый объект как копию существующего (T b = a;илиT b(a);). - Оператор копирующего присваивания
T& operator=(const T&)- заменяет содержимое уже созданного объекта копией другого (b = a;).
Ещё две присоединяются к ним в современном C++ (операции перемещения, с которыми вы встретились в предыдущей теме): конструктор перемещения T(T&&) и оператор перемещающего присваивания T& operator=(T&&). Вместе эти пять - предмет правила пяти.
int это копирует значение; для сырого указателя это копирует адрес, а не память, на которую он указывает. Именно этот факт - корень всех ошибок в этой теме.