← Назад к вопросам

Как защитить объект от копирования в C++?

1.6 Junior🔥 71 комментариев
#ООП и проектирование#Умные указатели и управление памятью#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как защитить объект от копирования в C++

В C++ каждый класс по умолчанию получает компилятор-сгенерированные методы копирования: конструктор копирования (copy constructor) и оператор присваивания (copy assignment operator). Иногда нужно запретить копирование объектов, например для объектов, управляющих уникальными ресурсами.

Способ 1: Удаление методов (C++11 и выше) - РЕКОМЕНДУЕТСЯ

#include <iostream>

class NonCopyable {
public:
    NonCopyable() = default;
    
    // Запретить копирование
    NonCopyable(const NonCopyable&) = delete;           // Конструктор копирования
    NonCopyable& operator=(const NonCopyable&) = delete; // Оператор присваивания
    
    void doSomething() { std::cout << "Doing work" << std::endl; }
};

int main() {
    NonCopyable obj1;
    obj1.doSomething();
    
    NonCopyable obj2 = obj1;  // ОШИБКА компиляции!
    NonCopyable obj3;
    obj3 = obj1;              // ОШИБКА компиляции!
    
    return 0;
}

Преимущества:

  • Явно показывает намерение ("deleted" лучше, чем просто "private")
  • Ошибка компиляции четкая
  • Используется во всем современном C++

Способ 2: Запрет перемещения (Move Semantics)

Иногда нужно запретить не только копирование, но и перемещение (C++11):

class StrictlyNonCopyable {
public:
    StrictlyNonCopyable() = default;
    
    // Запретить копирование
    StrictlyNonCopyable(const StrictlyNonCopyable&) = delete;
    StrictlyNonCopyable& operator=(const StrictlyNonCopyable&) = delete;
    
    // Запретить перемещение
    StrictlyNonCopyable(StrictlyNonCopyable&&) = delete;
    StrictlyNonCopyable& operator=(StrictlyNonCopyable&&) = delete;
};

int main() {
    StrictlyNonCopyable obj;
    
    auto obj2 = std::move(obj);  // ОШИБКА!
    
    return 0;
}

Способ 3: Приватные методы (C++98 и выше) - СТАРЫЙ СПОСОБ

class OldStyleNonCopyable {
public:
    OldStyleNonCopyable() = default;
    
    void doSomething() { std::cout << "Work" << std::endl; }
    
private:
    // Скрыть от пользователей
    OldStyleNonCopyable(const OldStyleNonCopyable&);           // Не реализовано
    OldStyleNonCopyable& operator=(const OldStyleNonCopyable&); // Не реализовано
};

int main() {
    OldStyleNonCopyable obj1;
    OldStyleNonCopyable obj2 = obj1;  // ОШИБКА: недоступно (приватно)
    
    return 0;
}

Минусы:

  • Старомодный подход
  • Ошибка менее информативна
  • Не очень понятно, что сделано намеренно

Способ 4: Наследование от базового класса

#include <iostream>

// Базовый класс, запрещающий копирование
class NonCopyableBase {
protected:
    NonCopyableBase() = default;
    
public:
    NonCopyableBase(const NonCopyableBase&) = delete;
    NonCopyableBase& operator=(const NonCopyableBase&) = delete;
};

// Наследуем и автоматически запрещаем копирование
class MyResource : public NonCopyableBase {
public:
    void doSomething() { std::cout << "Working" << std::endl; }
};

int main() {
    MyResource r1;
    MyResource r2 = r1;  // ОШИБКА: delete из базового класса!
    
    return 0;
}

Используется в стандартной библиотеке:

  • std::mutex - нельзя копировать (управляет мьютексом)
  • std::fstream - нельзя копировать (управляет файлом)
  • std::unique_ptr - нельзя копировать (уникальное владение)

Практический пример: Управление ресурсом

#include <iostream>
#include <fstream>
#include <thread>

// Файловый обработчик - не должен копироваться
class FileHandler {
pubate:
    // Запретить копирование
    FileHandler(const FileHandler&) = delete;
    FileHandler& operator=(const FileHandler&) = delete;
    
    // Разрешить перемещение (C++11)
    FileHandler(FileHandler&&) = default;
    FileHandler& operator=(FileHandler&&) = default;
    
public:
    FileHandler(const std::string& filename) : file(filename) {}
    
    void write(const std::string& data) {
        file << data << std::endl;
    }
    
    ~FileHandler() {
        file.close();
    }
    
private:
    std::ofstream file;
};

int main() {
    FileHandler handler("output.txt");
    handler.write("Hello");
    
    // ОШИБКА: нельзя копировать
    // FileHandler copy = handler;  // ОШИБКА!
    
    // ПРАВИЛЬНО: перемещение разрешено
    FileHandler moved = std::move(handler);
    moved.write("World");
    
    return 0;
}

Разрешить копирование, но запретить перемещение

class CopyableOnly {
public:
    CopyableOnly() = default;
    CopyableOnly(const CopyableOnly&) = default;           // Копировать можно
    CopyableOnly& operator=(const CopyableOnly&) = default;
    
    // Запретить перемещение
    CopyableOnly(CopyableOnly&&) = delete;
    CopyableOnly& operator=(CopyableOnly&&) = delete;
};

Сравнение подходов

ПодходC++ стандартЯсностьПроизводительностьИспользование
deleteC++11+ОтличнаяОтличнаяРЕКОМЕНДУЕТСЯ
private методыC++98+СредняяХорошаяСтарый код
НаследованиеC++11+ХорошаяОтличнаяДля иерархий
Умные указателиC++11+ХорошаяХорошаяС динамической памятью

Когда запретить копирование

  1. Управление уникальными ресурсами:

    • Мьютексы (std::mutex)
    • Файлы (std::fstream)
    • Сокеты, соединения с БД
    • Потоки (std::thread)
  2. Монитор-паттерн:

    • Только один активный экземпляр
  3. Управление памятью:

    • Когда new и delete внутри класса
  4. Синхронизация:

    • Объекты, защищённые мьютексом

Резюме

Защитить объект от копирования в C++:

class NonCopyable {
public:
    NonCopyable() = default;
    
    // Удаляем копирование (C++11+)
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
    
    // Опционально: удаляем перемещение
    NonCopyable(NonCopyable&&) = delete;
    NonCopyable& operator=(NonCopyable&&) = delete;
};

Ключевые моменты:

  • Используй = delete (современный и ясный подход)
  • Всегда удаляй оба: конструктор копирования и оператор присваивания
  • Подумай о перемещении (move semantics) в C++11+
  • Наследование от NonCopyable базового класса - элегантное решение
  • Запрещай копирование для уникальных ресурсов (файлы, сокеты, мьютексы)