<MyRusakov.ru />

WordPress 6 с Нуля до Гуру

WordPress 6 с Нуля до Гуру

Этот курс научит Вас созданию самых разных сайтов на самой популярной в мире CMS - WordPress. Вы увидите установку и настройку локального сервера, разбор каждой настройки, каждой кнопки и каждого пункта меню в панели WordPress.

Также Вы получите и всю практику, поскольку прямо в курсе с нуля создаётся полноценный Интернет-магазин, который затем публикуется в Интернете. И всё это прямо на Ваших глазах.

Помимо уроков к курсу идут упражнения для закрепления материала.

И, наконец, к курсу идёт ценнейший Бонус по тому, как используя ChatGPT и создавая контент для сайта, можно выйти на пассивный доход. Вы наглядно увидите, как зарегистрироваться в ChatGPT (в том числе, и если Вы из России), как правильно выбрать тему для сайта, как правильно генерировать статьи для него(чтобы они индексировались поисковыми системами) и как правильно монетизировать трафик на сайте.

Подробнее
Подписка

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

YouTube Подписаться

Подписавшись по E-mail, Вы будете получать уведомления о новых статьях.

Подписка Подписаться

Добавляйтесь ко мне в друзья ВКонтакте! Отзывы о сайте и обо мне оставляйте в моей группе.

Мой аккаунт Мой аккаунт Моя группа
Опрос

Какая тема Вас интересует больше?

Правило трёх, пяти и нуля в C++: простое практическое руководство с примерами

Правило трёх, пяти и нуля в C++: простое практическое руководство с примерами

Запрос: правило трёх в C++, правило пяти в C++, правило нуля в C++ — понятное руководство с примерами и лучшими практиками.

Зачем нужно правило трёх, пяти и нуля в C++

Если ваш класс владеет ресурсом (динамическая память, файл, мьютекс), нужно явно управлять жизненным циклом. Отсюда три варианта:

  • Правило трёх: если вы определяете один из этих членов — деструктор, конструктор копирования, оператор присваивания копированием — скорее всего, должны определить все три.
  • Правило пяти: в C++11+ добавились перемещающие конструктор и оператор присваивания. Если вы пишете особые члены, подумайте и о перемещении.
  • Правило нуля: лучший вариант — ничего не писать. Делегируйте владение RAII-типа (например, std::vector или std::unique_ptr), и компилятор сгенерирует корректные операции сам.

Пример: Правило трёх — глубокое копирование ресурса

Класс владеет динамическим массивом. Реализуем деструктор, конструктор копирования и оператор присваивания копированием.

#include <iostream>
#include <algorithm>
#include <cstddef>

class Buffer {
    std::size_t size_{};
    int* data_{};
public:
    explicit Buffer(std::size_t n)
        : size_(n), data_(n ? new int[n] : nullptr) {
        if (data_) std::fill(data_, data_ + size_, 0);
    }

    ~Buffer() {
        delete[] data_;
    }

    Buffer(const Buffer& other)
        : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) {
        if (size_) std::copy(other.data_, other.data_ + size_, data_);
        std::cout << "copy ctor\n";
    }

    Buffer& operator=(const Buffer& other) {
        if (this == &other) return *this; // самоприсваивание
        int* newData = other.size_ ? new int[other.size_] : nullptr;
        if (other.size_) std::copy(other.data_, other.data_ + other.size_, newData);
        delete[] data_;
        data_ = newData;
        size_ = other.size_;
        std::cout << "copy assign\n";
        return *this;
    }

    int& operator[](std::size_t i) { return data_[i]; }
    const int& operator[](std::size_t i) const { return data_[i]; }
    std::size_t size() const { return size_; }
};

Теперь копирование создаёт независимую копию массива, а утечек нет.

Правило пяти: добавляем перемещение и ускоряем контейнеры

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

#include <vector>
#include <iostream>

class Buffer {
    std::size_t size_{};
    int* data_{};
public:
    explicit Buffer(std::size_t n)
        : size_(n), data_(n ? new int[n] : nullptr) {}
    ~Buffer() { delete[] data_; }

    Buffer(const Buffer& other)
        : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) {
        if (size_) std::copy(other.data_, other.data_ + size_, data_);
        std::cout << "copy ctor\n";
    }
    Buffer& operator=(const Buffer& other) {
        if (this == &other) return *this;
        int* newData = other.size_ ? new int[other.size_] : nullptr;
        if (other.size_) std::copy(other.data_, other.data_ + other.size_, newData);
        delete[] data_;
        data_ = newData;
        size_ = other.size_;
        std::cout << "copy assign\n";
        return *this;
    }

    // Перемещающий конструктор и оператор (важно: noexcept)
    Buffer(Buffer&& other) noexcept : size_(other.size_), data_(other.data_) {
        other.size_ = 0; other.data_ = nullptr;
        std::cout << "move ctor\n";
    }
    Buffer& operator=(Buffer&& other) noexcept {
        if (this == &other) return *this;
        delete[] data_;
        size_ = other.size_;
        data_ = other.data_;
        other.size_ = 0; other.data_ = nullptr;
        std::cout << "move assign\n";
        return *this;
    }
};

int main() {
    std::vector<Buffer> v;
    v.push_back(Buffer(10)); // move ctor
    v.push_back(Buffer(20)); // move ctor + при росте емкости: move старых элементов
    v.push_back(Buffer(30)); // ещё перемещения
}

Если убрать noexcept у перемещающих операций, многие контейнеры будут копировать элементы при перераспределении памяти, что медленнее.

Альтернатива: copy-and-swap для простоты и надёжности

Идиома copy-and-swap упрощает оператор присваивания: принимаем параметр по значению (копия или перемещение), а затем меняем содержимое местами. Это исключает дублирование кода и даёт строгую гарантию безопасности при исключениях.

#include <utility>

class BufferCS {
    std::size_t size_{};
    int* data_{};
public:
    BufferCS() = default;
    explicit BufferCS(std::size_t n) : size_(n), data_(n ? new int[n] : nullptr) {}
    ~BufferCS() { delete[] data_; }
    BufferCS(const BufferCS& other)
        : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) {
        if (size_) std::copy(other.data_, other.data_ + size_, data_);
    }
    BufferCS(BufferCS&& other) noexcept : size_(other.size_), data_(other.data_) {
        other.size_ = 0; other.data_ = nullptr;
    }

    friend void swap(BufferCS& a, BufferCS& b) noexcept {
        using std::swap;
        swap(a.size_, b.size_);
        swap(a.data_, b.data_);
    }

    BufferCS& operator=(BufferCS other) { // копия или перемещение сюда
        swap(*this, other);
        return *this;
    }
};

Когда выбирать правило нуля: используем RAII-типы

Если можно, не управляйте сырыми ресурсами вручную. Делегируйте ответственность готовым RAII-обёрткам — и ваши классы будут автоматически корректно копироваться и перемещаться.

#include <vector>

class SafeBuffer {
    std::vector<int> data_;
public:
    explicit SafeBuffer(std::size_t n, int value = 0) : data_(n, value) {}
    int& operator[](std::size_t i) { return data_[i]; }
    const int& operator[](std::size_t i) const { return data_[i]; }
    std::size_t size() const { return data_.size(); }
    // Никаких специальных членов: правило нуля!
};

Там, где владение эксклюзивное, используйте std::unique_ptr — копирование запретите, перемещение разрешите.

#include <memory>
#include <cstddef>

class UniqueBuf {
    std::unique_ptr<int[]> data_;
    std::size_t size_{};
public:
    explicit UniqueBuf(std::size_t n)
        : data_(n ? std::make_unique<int[]>(n) : nullptr), size_(n) {}
    UniqueBuf(const UniqueBuf&) = delete;
    UniqueBuf& operator=(const UniqueBuf&) = delete;
    UniqueBuf(UniqueBuf&&) noexcept = default;
    UniqueBuf& operator=(UniqueBuf&&) noexcept = default;
};

Частые ошибки и советы

  • Нет noexcept у перемещения: контейнеры будут копировать — добавляйте noexcept, если это безопасно.
  • Копирование вместо перемещения из const: перемещающий конструктор не принимает const rvalue. Код std::move(constObj) вызовет копирование.
  • Необработанное самоприсваивание: или защищайтесь if (this == &rhs), или используйте copy-and-swap.
  • Дублирование логики: вынесите общую логику в функцию, либо примените copy-and-swap.
  • Пытаться управлять сырой памятью без нужды: сначала подумайте о правиле нуля и RAII-типах.
  • =default и =delete: явно указывайте намерения. Упростит код и диагностику.

Мини-чеклист по правилам 3/5/0

  • Класс владеет ресурсом напрямую? Реализуйте правило пяти (или как минимум трёх).
  • Можно доверить ресурс std::vector/std::string/std::unique_ptr? Выбирайте правило нуля.
  • Эксклюзивное владение? Скопировать нельзя: пометьте копирование как =delete, перемещение =default noexcept.
  • Оператор присваивания громоздкий? Рассмотрите copy-and-swap.
  • Проверьте производительность контейнеров: есть ли noexcept у перемещения?

Практический тест: почему noexcept важен для std::vector

Соберите пример с/без noexcept и посмотрите в вывод: при увеличении ёмкости vector будет либо перемещать элементы (move ctor), либо копировать (copy ctor). На больших объектах разница значительна.

Что почитать и куда двигаться дальше

Освоив правило трёх, пяти и нуля, вы избежите утечек и получите предсказуемую производительность. Дальше рекомендую углубиться в семантику перемещения, исключения, PIMPL и проектирование API классов.

Хотите системно и последовательно прокачать C++ с практикой? Загляните в курс Программирование на C++ с Нуля до Гуру — посмотреть программу и начать обучение.

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (https://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: https://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: https://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как создать свой сайт

  2. Текстовая ссылка:

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Комментарии (0):

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.