Ответ
Правило трёх (Rule of Three) — это принцип C++, который гласит: если классу требуется явно определённый пользовательский деструктор, конструктор копирования или копирующий оператор присваивания, то, вероятно, ему требуются все три. Это следствие необходимости корректного управления ресурсами (память, файловые дескрипторы, сокеты и т.д.).
Почему это важно? Без явного определения этих методов компилятор сгенерирует их автоматически (поверхностное копирование). Для классов, владеющих сырыми указателями, это приводит к двойному освобождению памяти или утечкам.
Пример нарушения и исправления:
// НАРУШЕНИЕ: Класс с сырым указателем и только деструктором.
class BadString {
char* data;
public:
BadString(const char* str) {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
~BadString() { delete[] data; } // Только деструктор!
// Проблема: компилятор сгенерирует конструктор копирования и оператор присваивания,
// которые просто скопируют указатель. Два объекта будут указывать на одну область памяти.
};
// СОБЛЮДЕНИЕ ПРАВИЛА ТРЁХ:
class String {
char* data;
public:
String(const char* str = "") {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
~String() { delete[] data; } // 1. Деструктор
// 2. Конструктор копирования (глубокое копирование)
String(const String& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
// 3. Копирующий оператор присваивания (с проверкой на самоприсваивание)
String& operator=(const String& other) {
if (this != &other) { // Защита от самоприсваивания (a = a)
delete[] data; // Освобождаем старые ресурсы
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
return *this;
}
};
Современный C++ (C++11 и новее): Правило пяти (Rule of Five) расширяет правило тремя, добавляя конструктор перемещения и перемещающий оператор присваивания для эффективной работы с rvalue-ссылками. Лучшей практикой является использование Правила нуля (Rule of Zero): проектировать классы так, чтобы управление ресурсами делегировалось умным указателям (std::unique_ptr, std::shared_ptr) и стандартным контейнерам, тогда все пять специальных функций будут сгенерированы корректно автоматически.