programowanieProgramista C++

Czym różnią się shallow copy i deep copy w C++? W jakich sytuacjach warto samodzielnie zaimplementować głębokie kopiowanie i jak to zrobić poprawnie?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Shallow copy (powierzchowne kopiowanie) – kopiuje tylko wartości pól obiektu, w tym wskaźniki, ale nie duplikuje pamięci przydzielonej dynamicznie. W konsekwencji oba obiekty wskazują na ten sam obszar pamięci. Może to prowadzić do błędów przy modyfikacji danych lub zwalnianiu pamięci.

Deep copy (głębokie kopiowanie) – tworzy pełną kopię wszystkich zasobów, na które wskazuje obiekt, aby każdy obiekt miał swoją kopię danych.

Kiedy potrzebny jest deep copy:

  • Gdy obiekt posiada pamięć przydzieloną dynamicznie.
  • Gdy zmiana zawartości jednego obiektu nie powinna wpływać na inny.

Przykład implementacji deep copy:

class ArrayHolder { public: ArrayHolder(size_t size) : sz(size), data(new int[size]) {} ~ArrayHolder() { delete[] data; } // Konstructor kopiujący głęboko ArrayHolder(const ArrayHolder& other) : sz(other.sz), data(new int[other.sz]) { std::copy(other.data, other.data + other.sz, data); } // Przypisanie kopiujące głęboko 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; };

Pytanie z podstępem.

Pytanie: Czy konstruktor kopiujący będzie poprawnie działał, jeśli po prostu skopiujemy wartość wskaźnika?

Odpowiedź: Nie, prowadzi to do podwójnego zwolnienia pamięci przy zniszczeniu obu obiektów. Należy wdrożyć głębokie kopiowanie dla prawidłowego zarządzania zasobami.

Przykłady rzeczywistych błędów z powodu braku znajomości szczegółów tematu.


Historia

W jednym z projektów do przekazywania tablic między modułami używano obiektów klasy, w której domyślny konstruktor kopiujący kopiował tylko wskaźnik (shallow copy). W rezultacie, po wyjściu tymczasowego obiektu z zakresu widoczności tablica została zniszczona, a główny obiekt pozostał z "wiszącym" wskaźnikiem, co prowadziło do nieokreślonego zachowania i awarii aplikacji.


Historia

W bibliotece do pracy z obrazami wielokrotnie kopiowano obiekty zawierające dynamicznie przydzielone bufory. Z powodu braku deep copy po zwolnieniu pamięci w jednym obiekcie inny uzyskiwał dostęp do zwolnionego obszaru. Przejawiała się to jako uszkodzenia danych oraz awarie przy usuwaniu obiektu.


Historia

Podczas integracji przestarzałego kodu z nowoczesnymi standardami w starych klasach nie zaimplementowano ani konstruktora kopiującego, ani operatora przypisania. Użycie tych klas z kontenerami biblioteki standardowej prowadziło do nieoczekiwanych awarii, ponieważ kontenery kopiowały obiekty powierzchownie.