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 после освобождения памяти в одном объекте другой получал доступ к освобожденной области. Проявлялись как повреждения данных, так и крэши при удаления объекта.
История
При интеграции устаревшего кода с современными стандартами, в старых классах не были реализованы ни копирующий конструктор, ни оператор присваивания. Использование этих классов с контейнерами стандартной библиотеки приводило к неожиданным сбоям, так как контейнеры копировали объекты поверхностно.