본문 바로가기
프로그래밍/C\C++

C/C++ 얕은 복사 및 깊은 복사 내용 정리

by 허구의 2025. 6. 21.
728x90

 

 

얕은 복사와 깊은 복사: C/C++ 메모리 복사의 진짜 의미

C/C++에서 변수 복사는 단순히 '=' 기호 하나로 끝나지 않습니다. 복사의 방식에 따라 프로그램의 동작, 성능, 심지어 심각한 오류까지 좌우될 수 있습니다. 이 글에서는 얕은 복사(shallow copy)깊은 복사(deep copy)의 차이를 근본부터 파헤쳐 보겠습니다.

복사란 무엇인가?

복사란 한 객체의 내용을 다른 객체로 복제하는 것입니다. 하지만 '내용'이라는 말이 문제가 됩니다. 객체가 단순한 값(int, double 등)일 때는 문제가 없지만, 객체가 포인터나 동적 메모리를 포함할 때 복사의 정의는 복잡해집니다.

int a = 10;
int b = a;  // 단순 복사 (값 복사)

 

이 경우 ab는 완전히 독립된 두 개의 변수입니다.

얕은 복사란?

얕은 복사(shallow copy)는 객체의 값을 단순히 비트 단위로 복사하는 것입니다. 포인터가 포함되어 있을 경우 포인터 값 (즉, 메모리 주소)만 복사되며, 실제 데이터는 공유됩니다.

struct MyClass {
    int* data;
};

MyClass a;
a.data = new int(42);
MyClass b = a;  // 얕은 복사

 

위 코드에서 b.dataa.data같은 메모리 주소를 가리킵니다. 즉, 둘이 동일한 데이터(42)를 공유합니다.

얕은 복사의 위험

  • 한 객체에서 메모리를 해제하면 다른 객체가 무효화됨 (Dangling Pointer)
  • 동시 수정 시 의도치 않은 결과 발생
  • 더블 delete 가능성

깊은 복사란?

깊은 복사(deep copy)는 객체가 소유한 모든 리소스까지 독립적으로 새롭게 복사하는 것입니다. 포인터가 가리키는 데이터까지 새로 복사하여 두 객체가 완전히 독립된 메모리를 가지게 만듭니다.

struct MyClass {
    int* data;

    // 깊은 복사 생성자
    MyClass(const MyClass& other) {
        data = new int(*(other.data));
    }

    // 소멸자
    ~MyClass() { delete data; }
};

 

이제 ab는 동일한 값을 가지고 있지만, 서로 다른 메모리 공간에 존재합니다. 한 쪽이 소멸되어도 다른 쪽은 영향을 받지 않습니다.

얕은 복사 vs 깊은 복사 비교

특징 얕은 복사 깊은 복사
포인터 멤버 주소 복사 실제 데이터 복사
메모리 독립성 공유 완전 독립
자원 해제 다중 해제 위험 안전
비용 저렴 비쌈

C++에서 복사의 기본 동작: 컴파일러가 만들어주는 얕은 복사

C++에서는 특별히 복사 생성자, 대입 연산자를 작성하지 않으면 컴파일러가 기본 복사 생성자기본 대입 연산자를 만들어주는데, 이들은 기본적으로 **얕은 복사**를 수행합니다.

MyClass a;
MyClass b = a;  // 컴파일러가 얕은 복사 생성자 자동 생성

 

따라서 리소스를 소유하는 객체를 만들 때는 반드시 복사 생성자, 대입 연산자, 소멸자를 명시적으로 정의해야 합니다. (Rule of Three)

Rule of Three / Rule of Five

리소스를 소유하는 클래스라면 반드시 다음 세 가지를 정의해야 안전합니다:

  • 복사 생성자
  • 대입 연산자
  • 소멸자

이를 Rule of Three라 부릅니다.

C++11 이후 이동 생성자, 이동 대입 연산자까지 포함하면 Rule of Five로 확장됩니다.

스마트 포인터와 깊은 복사의 부담 완화

스마트 포인터(std::unique_ptr, std::shared_ptr)를 사용하면 깊은 복사의 구현 부담이 크게 줄어듭니다. 스마트 포인터는 소유권과 복사 정책을 내부에서 잘 관리해주므로 많은 경우 직접 복사 생성자를 구현할 필요가 사라집니다.

복사 전략 선택 기준

  • 리소스를 소유하지 않는 단순 객체 → 기본 얕은 복사 충분
  • 동적 메모리 직접 소유 → 깊은 복사 필요
  • 스마트 포인터 사용 → 복사 부담 최소화
  • 대규모 리소스 → 복사보다는 Move Semantics 적극 활용

 

감사합니다.

728x90