Ответ
Правило нуля — это современный принцип проектирования классов в C++, который гласит: не определяйте собственные деструктор, конструкторы копирования/перемещения и операторы присваивания копированием/перемещением, если в этом нет абсолютной необходимости. Вместо этого делегируйте управление ресурсами классам-членам, которые сами корректно их обрабатывают (например, стандартным контейнерам и умным указателям). Компилятор автоматически сгенерирует все специальные функции-члены, которые будут вести себя корректно и эффективно.
Сравнение с Правилом трёх/пяти:
- Правило трёх (до C++11): Если классу нужен пользовательский деструктор, конструктор копирования или оператор присваивания копированием, то, вероятно, нужны все три.
- Правило пяти (C++11 и выше): Добавляет конструктор перемещения и оператор присваивания перемещением.
- Правило нуля: Предпочтительнее. Избегайте написания этих функций вручную, используя RAII.
Пример соблюдения Правила нуля:
#include <memory>
#include <vector>
#include <string>
class ResourceManager {
private:
// Члены-классы сами управляют своими ресурсами.
std::vector<int> data_; // Управляет своей памятью.
std::unique_ptr<NetworkHandle> connection_; // Владение уникальным ресурсом.
std::shared_ptr<Cache> sharedCache_; // Разделяемое владение.
std::string name_; // Управляет строкой.
public:
ResourceManager(std::vector<int> d, std::string n)
: data_(std::move(d)), name_(std::move(n)) {}
// НЕТ пользовательского деструктора, конструкторов копирования/перемещения,
// операторов присваивания. Компилятор сгенерирует их автоматически, и они
// будут корректно вызывать соответствующие функции для каждого члена.
void processData() {
// Работа с данными...
}
};
int main() {
ResourceManager rm1({1, 2, 3}, "First");
auto rm2 = rm1; // Используется сгенерированный компилятором конструктор копирования.
// Он скопирует вектор и строку, но unique_ptr скопировать нельзя,
// поэтому эта строка не скомпилируется — что и правильно!
auto rm3 = std::move(rm1); // Используется сгенерированный конструктор перемещения.
// Ресурсы эффективно перемещены из rm1 в rm3.
return 0;
}
Преимущества Правила нуля:
- Упрощение кода: Меньше boilerplate-кода для написания и поддержки.
- Повышение надёжности: Исключаются ошибки, связанные с ручным управлением памятью и ресурсами (утечки, двойное удаление).
- Оптимальная производительность: Компилятор может генерировать более эффективные тривиальные (trivial) функции или функции
noexceptдля перемещения. - Безопасность при исключениях: Классы-члены со строгой гарантией исключений обеспечивают её и для вашего класса.