Я бы сказал, что Правило Три становится Правилом Три, Четыре и Пять:
Каждый класс должен явно определять точно одну из следующего набора специальных функций-членов:
- Нет
- Деструктор, конструктор копирования, оператор назначения копирования
Кроме того, каждый класс, который явно определяет деструктор, может явно определять конструктор перемещения и / или перемещениеоператор присваивания.
Обычно целесообразно использовать один из следующих наборов специальных функций-членов:
- Нет (для многих простых классов, где неявно генерируемые специальные функции-члены являются правильными и быстрыми)
- Деструктор, конструктор копирования, оператор назначения копирования (в этом случае класс не будет подвижным)
- Деструктор, конструктор перемещения, оператор назначения перемещения (в этом случае класс не будет копируемым,полезно для классов управления ресурсами, где базовый ресурс не копируется)
- Деструктор, конструкция копированияили, оператор присваивания копии, конструктор перемещения (из-за исключения копирования нет затрат, если оператор присваивания копии принимает свой аргумент по значению)
- Деструктор, конструктор копирования, оператор копирования, конструктор перемещения, оператор назначения перемещения
Обратите внимание, что конструктор перемещения и оператор присваивания перемещения не будут созданы для класса, который явно объявляет любую из других специальных функций-членов, конструктор копирования и оператор присваивания копирования не будутгенерируется для класса, который явно объявляет конструктор перемещения или оператор присваивания перемещения, и что класс с явно объявленным деструктором и неявно определенным конструктором копирования или неявно определенным оператором присваивания копии считается устаревшим.В частности, следующий совершенно правильный C ++ 03 полиморфный базовый класс
class C {
virtual ~C() { } // allow subtype polymorphism
};
должен быть переписан следующим образом:
class C {
C(const C&) = default; // Copy constructor
C(C&&) = default; // Move constructor
C& operator=(const C&) = default; // Copy assignment operator
C& operator=(C&&) = default; // Move assignment operator
virtual ~C() { } // Destructor
};
Немного раздражает, но, вероятно, лучше, чем альтернативный (автоматическийсоздание всех специальных функций-членов).
В отличие от правила «большой тройки», где несоблюдение этого правила может привести к серьезному ущербу, обычно явно не объявляется конструктор перемещения и оператор назначения перемещения, ночасто неоптимальный по эффективности.Как упоминалось выше, конструктор перемещения и операторы присваивания перемещений генерируются только в том случае, если явно не объявлен конструктор копирования, оператор присваивания копии или деструктор.Это не симметрично традиционному поведению C ++ 03 в отношении автоматической генерации конструктора копирования и оператора назначения копирования, но намного безопаснее.Таким образом, возможность определять конструкторы перемещения и операторы присваивания перемещения очень полезна и создает новые возможности (чисто подвижные классы), но классы, которые придерживаются правила C ++ 03 «большой тройки», все еще будут в порядке.
Для классов управления ресурсами вы можете определить конструктор копирования и оператор назначения копирования как удаленные (что считается определением), если базовый ресурс не может быть скопирован.Часто вы все еще хотите переместить конструктор и оператор присваивания перемещения.Операторы назначения копирования и перемещения часто будут реализованы с использованием swap
, как в C ++ 03.Если у вас есть конструктор перемещения и оператор присваивания перемещения, специализация std::swap
станет неважной, так как универсальный std::swap
использует конструктор перемещения и оператор присваивания перемещения, если он доступен, и это должно быть достаточно быстро.
Классы, которыене предназначены для управления ресурсами (т. е. без непустого деструктора) или полиморфизма подтипа (т. е. без виртуального деструктора), не должны объявлять ни одну из пяти специальных функций-членов;все они будут сгенерированы автоматически и будут вести себя правильно и быстро.