ПрограммированиеC++ разработчик

Что такое конструктор копирования и оператор присваивания? Как избежать ошибок при копировании объектов с динамической памятью?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Конструктор копирования и оператор копирующего присваивания необходимы для работы с объектами, владеющими ресурсами (например, динамической памятью). По умолчанию компилятор сгенерирует поле-поле копирование, что небезопасно для сырого указателя:

class Buffer { public: Buffer(size_t size) { data = new int[size]; this->size = size; } ~Buffer() { delete[] data; } // Правильная реализация конструктора копирования Buffer(const Buffer& other) : size(other.size) { data = new int[size]; std::copy(other.data, other.data + size, data); } // Правильный оператор присваивания Buffer& operator=(const Buffer& other) { if (this != &other) { delete[] data; size = other.size; data = new int[size]; std::copy(other.data, other.data + size, data); } return *this; } private: int* data; size_t size; };

Иначе при копировании двух объектов один освобождает память, другой остается с висячим указателем (double free или use after free).

Вопрос с подвохом

Что произойдет, если явно объявить только конструктор копирования, но не оператор присваивания? Когда он нужен, а когда — нет?

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

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

Примеры реальных ошибок из-за незнания тонкостей темы


История

В медиасервере при копировании объекта буфера использовали копирующий конструктор, но забыли про оператор присваивания. Когда один буфер присваивали другому, происходило двойное освобождение памяти при разрушении объектов. Ошибка проявилась при стрессовых тестах.


История

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


История

В одном из проектов с графическими ресурсами забыли реализовать конструктор копирования при передаче объектов между потоками. Передача по значению приводила к shallow copy, и после изменения объекта в другом потоке возникало повреждение данных и падение программы.