← Назад к вопросам
Как защитить объект от копирования в 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++ стандарт | Ясность | Производительность | Использование |
|---|---|---|---|---|
| delete | C++11+ | Отличная | Отличная | РЕКОМЕНДУЕТСЯ |
| private методы | C++98+ | Средняя | Хорошая | Старый код |
| Наследование | C++11+ | Хорошая | Отличная | Для иерархий |
| Умные указатели | C++11+ | Хорошая | Хорошая | С динамической памятью |
Когда запретить копирование
-
Управление уникальными ресурсами:
- Мьютексы (
std::mutex) - Файлы (
std::fstream) - Сокеты, соединения с БД
- Потоки (
std::thread)
- Мьютексы (
-
Монитор-паттерн:
- Только один активный экземпляр
-
Управление памятью:
- Когда
newиdeleteвнутри класса
- Когда
-
Синхронизация:
- Объекты, защищённые мьютексом
Резюме
Защитить объект от копирования в 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 базового класса - элегантное решение
- Запрещай копирование для уникальных ресурсов (файлы, сокеты, мьютексы)