본문 바로가기
정상을향해/Languages

얕은복사와 깊은복사

by 사이테일 2013. 11. 11.

int num =20;

int &ref = num;

 

위와 같은 방식으로 변수와 참조자를 선언 및 초기화해 왔다.

 

하지만 C++에서는 다음의 방식으로 선언 및 초기화가 가능하다.

 

int num(20);

int &ref(num);

 

위의 두 가지 초기화 방식은 결과적으로 동일하다.

 

즉, C++에서는 위의 두 가지 초기화 방식을 동시에 지원하고 있다.

 

 

다음의 소스코드를 살펴보자.

 

<ClassInit.cpp>

 

31번째 줄에 나와있는,

 

SoSimple sim2 = sim1;

SoSimple sim2(sim1);

 

이 두 코드는 동일한 의미로 해석된다.

 

첫 번째 문장이 두 번째 문장의 형태로 묵시적으로 변환이 되어서 객체가 생성되는 것이다.

 

결과는 우리가 쉽게 상상할 수 있는 그대로의 결과가 출력된다.

 

 

여기서 생각해볼 점은 이러한 유형의 생성자가 정의되어 있지 않아도 똑같은 결과가 나온다는 점이다.

 

가령 15~19라인의 코드(복사생성자)를 삭제한다 하더라도 멤버 대 멤버의 복사가 진행된다.

 

즉, 복사 생성자를 정의하지 않으면, 멤버 대 멤버의 복사를 진행하는 디폴트 복사 생성자가 자동으로 삽입된다.

 

 

디폴트 복사 생성자는 멤버 대 멤버의 복사를 진행한다.

 

그리고 이러한 방식의 복사를 가리켜 '얕은 복사(shallow copy)'라 하는데,

 

이는 멤버변수가 힙의 메모리 공간을 참조하는 경우에 문제가 된다.

 

다음의 코드를 살펴보자.

 

<ShallowCopyError.cpp> 

 

앞서 설명한 것처럼 32라인에서 디폴트 복사 생성자가 실행되어 멤버 대 멤버 복사를 수행한다.

 

하지만 이 소스코드의 결과를 확인하면, 소멸자가 1번만 실행된다는 것을 알 수 있다.

 

그 문제는 바로 얕은 복사에 있다.

 

애초에 의도한 것과는 달리 디폴트 복사 생성자는 멤버 대 멤버를 단순히 복사만 하므로,

 

"Lee dong woo" 라는 하나의 문자열을 man1, man2 객체 모두 참조하게 되는 것이다.

 

즉, 객체 man2의 소멸로 인해 "Lee dong woo"라는 문자열이 소멸되고, man1 객체가 소멸되어야 하는데

 

man1 객체의 멤버 name이 참조하는 문자열이 이미 소멸된 상태이기 때문에 문제가 발생한다.

 

(이미 지워진 문자열을 대상으로 delete 연산을 하기 때문이다.)

 

따라서 복사 생성자를 정의할 때에는 이러한 문제가 발생하지 않도록 신경써야 한다.

 

 

 

이에 대한 해결방법은 여러가지가 있겠지만,

 

의도했던 형태의 복사가 진행되도록 복사 생성자를 정의하는 방법으로 문제를 해결해보겠다.

 

참고로 이러한 형태의 복사를 가리켜 '깊은 복사(deep copy)'라고 한다.

 

멤버뿐만 아니라 포인터로 참조하는 대상까지 깊게 복사한다는 뜻으로 정해진 이름이다.

 

다음과 같은 복사 생성자를 정의하면 쉽게 해결할 수 있다.

 

 

 

출처 : 열혈 C++ 프로그래밍 (윤성우 저)