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

Чем отличаются shallow copy и deep copy в C++? В каких ситуациях стоит реализовывать глубокое копирование самостоятельно, и как это правильно сделать?

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

Ответ.

Shallow copy (поверхностное копирование) – копирует только значения полей объекта, включая указатели, но не дублирует динамически выделяемую память. В результате, оба объекта указывают на один и тот же участок памяти. Это может привести к ошибкам при изменении данных или освобождении памяти.

Deep copy (глубокое копирование) – создает полную копию всех ресурсов, на которые ссылается объект, чтобы каждый объект владел своей копией данных.

Когда нужен deep copy:

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

Пример реализации deep copy:

class ArrayHolder { public: ArrayHolder(size_t size) : sz(size), data(new int[size]) {} ~ArrayHolder() { delete[] data; } // Deep copy constructor ArrayHolder(const ArrayHolder& other) : sz(other.sz), data(new int[other.sz]) { std::copy(other.data, other.data + other.sz, data); } // Deep copy assignment ArrayHolder& operator=(const ArrayHolder& other) { if (this != &other) { delete[] data; sz = other.sz; data = new int[sz]; std::copy(other.data, other.data + sz, data); } return *this; } private: size_t sz; int* data; };

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

Вопрос: Будет ли корректно работать копирующий конструктор, если в нем просто скопировать значение указателя?

Ответ: Нет, это приведет к двойному удалению памяти при разрушении обоих объектов. Необходимо реализовать глубокое копирование для корректного управления ресурсами.

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


История

На одном из проектов для передачи массивов между модулями использовались объекты класса, в котором копирующий конструктор по умолчанию копировал только указатель (shallow copy). В результате, после выхода временного объекта из области видимости массив разрушался, а основной объект оставался с «висячим» указателем, что приводило к неопределенному поведению и краху приложения.


История

В библиотеке для работы с изображениями неоднократно копировали объекты, содержащие динамически выделенные буферы. Из-за отсутствия deep copy после освобождения памяти в одном объекте другой получал доступ к освобожденной области. Проявлялись как повреждения данных, так и крэши при удаления объекта.


История

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