얕은 복사(Shallow copy)는 클래스의 멤버 값, 포인터를 포함하여 복사하지만, 이 포인터들이 가리키는 데이터는 복사하지 않습니다. 깊은 복사(Deep copy)는 포인터들이 가리키는 데이터의 새로운 복사본을 생성하여 메모리의 공유 소유와 이중 해제를 방지합니다.
예:
class Buffer { public: Buffer(size_t size) : size(size), data(new int[size]) {} // 얕은 복사(잘못됨) Buffer(const Buffer& other) : size(other.size), data(other.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; } ~Buffer() { delete[] data; } private: size_t size; int* data; };
클래스가 동적 배열을 가리키는 포인터를 포함하고 있다면, 왜 기본 복사 생성자와 할당 연산자를 그대로 복사하는 것으로 충분하지 않은가요?
올바른 답변:
기본 복사는 얕은 복사를 구현합니다. 두 개의 객체가 동일한 배열을 가리키게 되며, 삭제될 때 동일한 메모리를 두 번 해제하려고 시도하여(이중 해제) 프로그램이 충돌할 수 있습니다.
이야기 대규모 서버 애플리케이션에서 동적 픽셀 버퍼를 관리하는 자체 Image 클래스를 구현했습니다. 개발자들은 깊은 복사를 구현하지 않았고, Image 객체를 스레드 간에 값으로 전달할 때 메모리의 이중 해제가 발생하여 며칠에 한 번씩 시스템 충돌이 발생했습니다.
이야기 학생 프로젝트에서 동적으로 할당된 셀 배열을 가리키는 포인터를 포함한 Field 컨테이너를 사용했습니다. 배열 크기를 변경할 때 복사 생성자가 포인터만 복사하여 두 컨테이너가 동일한 메모리 영역을 사용하게 되었고, 이로 인해 일관성이 없는 데이터와 찾기 어려운 버그가 발생했습니다.
이야기 구식 C++ 그래픽 엔진 코드에서 자신의 소멸자와 할당 연산자를 구현하지 않은 Texture 클래스가 존재하여, 복사 및 임시 텍스처 할당 시 메모리 누수(memory leak)가 발생했습니다.