C++ Academy · Урок

Правило трёх/пяти

Правильно управляйте копированием и перемещением

Урок 3 из 413 шагов

«Правило трёх/пяти» — бесплатный урок C++ Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C++ Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C++ Academy содержит 4 уроков всего.

Правило трёх

Если классу нужен пользовательский деструктор, ему почти всегда также нужны пользовательские конструктор копирования и оператор присваивания копированием. Эти три элемента идут вместе.

Почему именно три

Класс, управляющий необладающим ресурсом, должен определить, как этот ресурс уничтожается, копируется и присваивается. Иначе поверхностные копии, созданные компилятором, приводят к двойному освобождению.

#include <iostream>

class Bad {
    int* p;
public:
    Bad(int v) : p(new int(v)) {}
    ~Bad() { delete p; }
    // default copy => two objects delete the same p!
};

Пользовательский конструктор копирования

Правильный конструктор копирования выполняет глубокое копирование, выделяя собственный ресурс вместо совместного использования.

#include <iostream>

class Box {
    int* p;
public:
    Box(int v) : p(new int(v)) {}
    Box(const Box& o) : p(new int(*o.p)) {}   // deep copy
    ~Box() { delete p; }
    int get() const { return *p; }
};

int main() {
    Box a(5);
    Box b = a;   // independent copy
    std::cout << b.get() << "\n";
}

Пользовательское присваивание копированием

Присваивание копированием должно освободить старый ресурс, защититься от присваивания самому себе и создать глубокую копию нового значения.

Box& operator=(const Box& o) {
    if (this != &o) {              // self-assignment guard
        delete p;
        p = new int(*o.p);
    }
    return *this;
}

Правило пяти

В C++11 появились операции перемещения. Если Вы определяете какую-либо из пяти операций (деструктор, конструктор копирования, присваивание копированием, конструктор перемещения, присваивание перемещением), рассмотрите возможность определить все пять.

Конструктор перемещения

Конструктор перемещения забирает ресурс у временного объекта и оставляет исходный объект пустым — это гораздо дешевле копирования.

Box(Box&& o) noexcept : p(o.p) {
    o.p = nullptr;   // source left empty
}

Присваивание перемещением

Присваивание перемещением освобождает текущий ресурс, а затем принимает владение ресурсом источника.

Box& operator=(Box&& o) noexcept {
    if (this != &o) {
        delete p;
        p = o.p;
        o.p = nullptr;
    }
    return *this;
}

Помечайте перемещения как noexcept

Операции перемещения следует объявлять как noexcept, чтобы такие контейнеры, как std::vector, могли безопасно использовать их при перераспределении памяти.

Правило нуля

Лучше всего вообще не владеть необработанными ресурсами. Используйте std::unique_ptr, std::vector или std::string — тогда Вам не придётся определять ни одну из пяти операций: значения по умолчанию, создаваемые компилятором, будут правильными.

#include <memory>

class Good {
    std::unique_ptr<int> p;   // owns the resource for you
public:
    Good(int v) : p(std::make_unique<int>(v)) {}
    // no destructor, no copy/move needed
};

Удаление операций

Если копирование не имеет смысла, запретите его: Box(const Box&) = delete;. Так тип намеренно становится доступным только для перемещения или вообще некопируемым.

Выбор подходящего правила

Стремитесь следовать правилу нуля. Переходите к правилу пяти только тогда, когда Вы действительно оборачиваете необработанный ресурс, для которого в стандартной библиотеке нет подходящего средства.

Быстрая проверка

Проверьте, насколько хорошо Вы понимаете правило трёх/пяти.

Итоги

Вы изучили правило трёх/пяти: управление необработанным ресурсом означает согласованное определение деструктора, операций копирования и перемещения. Помечайте операции перемещения как noexcept. Лучше всего следовать правилу нуля, используя стандартные типы RAII и не определяя ни одной из этих операций.

Можно начать бесплатно

Изучай C++ с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
51
Уроки
203

Часто задаваемые вопросы

Урок «Правило трёх/пяти» бесплатный?

Да — полный текст урока «Правило трёх/пяти» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C++ Academy, подпишись на CoddyKit PRO. Курс C++ Academy содержит 4 уроков всего.

Чему я научусь в уроке «Правило трёх/пяти»?

Правильно управляйте копированием и перемещением Ты практикуешь C++ Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C++ Academy?

Предыдущий опыт не требуется. C++ Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Правило трёх/пяти»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C++ Academy?

Да. Каждый урок C++ Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Принцип RAII
  2. Деструкторы и освобождение ресурсов
  3. Правило трёх/пяти
  4. Защитники области видимости
← Назад к C++ Academy