메모리 관리는 C++ 프로그래밍에서 가장 중요한 개념 중 하나입니다. 특히 객체를 복사할 때 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)의 차이를 이해하는 것은 매우 중요합니다. 이 글에서는 두 복사 방식의 차이점과 각각의 사용 사례에 대해 자세히 알아보겠습니다.
얕은 복사(Shallow Copy)란?
얕은 복사는 객체의 멤버 변수들을 비트 단위로 그대로 복사하는 방식입니다. C++에서 기본 제공되는 복사 생성자와 대입 연산자는 기본적으로 얕은 복사를 수행합니다.
class ShallowExample {
int* data;
public:
ShallowExample(int value) : data(new int(value)) {}
// 기본 복사 생성자는 얕은 복사 수행
};
ShallowExample original(42);
ShallowExample copy = original; // 얕은 복사 발생
이 경우, copy와 original 객체의 data 포인터는 같은 메모리 주소를 가리키게 됩니다.
얕은 복사의 장단점
장점:
- 복사 속도가 빠름
- 메모리 사용량이 적음
- 단순한 데이터 구조에 적합
단점:
- 포인터를 포함한 객체의 경우 메모리 해제 시 문제 발생 가능
- 한 객체의 변경이 다른 객체에 영향을 미침
- 소멸자에서 이중 해제 문제 발생 가능
깊은 복사(Deep Copy)란?
깊은 복사는 객체가 가리키는 모든 데이터를 새로운 메모리 공간에 복사하는 방식입니다. 포인터가 가리키는 실제 데이터까지 새로 할당하여 복사합니다.
class DeepExample {
int* data;
public:
DeepExample(int value) : data(new int(value)) {}
// 깊은 복사를 위한 복사 생성자
DeepExample(const DeepExample& other) {
data = new int(*other.data);
}
~DeepExample() {
delete data;
}
};
DeepExample original(42);
DeepExample copy = original; // 깊은 복사 발생
깊은 복사의 장단점
장점:
- 완전히 독립적인 객체 생성
- 메모리 관리가 안전함
- 한 객체의 변경이 다른 객체에 영향을 주지 않음
단점:
- 복사 속도가 상대적으로 느림
- 메모리 사용량이 증가
- 구현이 복잡할 수 있음
언제 어떤 복사 방식을 사용해야 할까?
얕은 복사가 적합한 경우
- 기본 데이터 타입만을 포함하는 클래스
- 참조 카운팅을 사용하는 스마트 포인터 기반 클래스
- 임시 객체를 생성하는 경우
- 복사 비용을 최소화해야 하는 성능 중심 코드
깊은 복사가 적합한 경우
- 동적 할당된 메모리를 관리하는 클래스
- 포인터 멤버 변수를 포함하는 클래스
- 독립적인 객체 복사가 필요한 경우
- 데이터 안정성이 중요한 경우
실제 사용 예시
// 스마트 포인터를 사용한 얕은 복사 예시
class SafeShallow {
std::shared_ptr<int> data;
public:
SafeShallow(int value) : data(std::make_shared<int>(value)) {}
// 기본 복사 생성자 사용 가능
};
// 깊은 복사가 필요한 클래스 예시
class ResourceManager {
char* buffer;
size_t size;
public:
ResourceManager(size_t n) : size(n), buffer(new char[n]) {}
// 깊은 복사 구현
ResourceManager(const ResourceManager& other) : size(other.size) {
buffer = new char[size];
std::memcpy(buffer, other.buffer, size);
}
~ResourceManager() {
delete[] buffer;
}
};
결론
얕은 복사와 깊은 복사는 각각의 용도가 있습니다. 단순한 값만 있는 클래스의 경우 얕은 복사로 충분하지만, 동적 메모리나 리소스를 관리하는 클래스의 경우 깊은 복사가 필요합니다.
클래스를 설계할 때는 해당 클래스의 특성과 사용 목적을 고려하여 적절한 복사 방식을 선택해야 합니다. 또한 현대 C++에서는 스마트 포인터와 이동 의미론과 같은 기능을 활용하여 더 안전하고 효율적인 코드를 작성할 수 있다는 점도 기억해두면 좋겠습니다.
'개발 이야기 > c++' 카테고리의 다른 글
🔒 C++ 싱글톤(Singleton) 패턴 이해하기 (2) | 2025.06.07 |
---|---|
C++ RAII 패턴 이해하기: 초보 개발자를 위한 가이드 (0) | 2024.12.08 |
CMake 초보자 가이드: 처음부터 시작하는 CMakeLists.txt 작성법 (0) | 2024.11.29 |
초보자를 위한 Makefile 작성 방법 (1) | 2024.11.28 |
C++에서 정적 라이브러리와 동적 라이브러리의 차이 (0) | 2024.11.28 |