본문 바로가기

Effective C++

항목 12. 객체의 모든 부분을 빠짐없이 복사하자.

 우선 예를 통해서 왜 객체의 모든 부분을 빠짐없이 복사해야 하는지 그 이유를 알아 봅시다. 

 일단 이 예제는 문제없이 출력 되는 것을 알 수 있습니다. 그럼 여기에서 일어날 수 있는 문제는 무엇일까요? 만약 Customer 클래스에 멤버 변수로 나이를 추가 한다고 해봅시다. int형 데이터 변수 age를 추가 시켜줄 경우에 복사 함수에서는 완전한 복사가 이루어지지 않고 부분 복사가 이루어집니다. 왜냐하면 지금은 name만 copy가 되고 있기 때문에, 새로 추가된 멤버 변수에 대해서는 따로 추가를 시켜 줘야 하기 때문이죠. 이러한 부분은 컴파일러가 해결해 주지 않습니다. 이러한 경우에 복사 함수에 일일이 추가로 구현을 해줘야 하는데 지금같은 경우에는 멤버 변수가 별로 없지만, 프로젝트가 커질 경우 이런 작업은 프로그래머에게 아주 번거로운 작업이 되겠죠. 
 하지만 지금같은 경우 더 심각한 문제가 발생할 수 있습니다. 바로 이 Customer 클래스를 상속받을 경우에 심각한 문제가 발생합니다. 아래와 같이 Customer를 상속받는 PriorityCustomer 클래스가 있다고 가정해 봅시다. (똑같이 복사 생성자, 대입 연산자 등을 지정해 줍니다.)

 실행해보면, 문제가 발생합니다. 현재 복사 생성자에서 현재 자신의 클래스(PriorityCustomer Class)만 초기화를 시켜 주고 있는데, 이런 경우에 기본 클래스는 기본 생성자를 호출 하게 됩니다. 그래서 name의 값이 제대로 대입이 안되고 있기 때문에 이런 문제가 발생하는 것입니다. 이 상황을 해결하기 위해서는 기본 클래스의 생성자도 호출을 해줘야 문제가 해결이 됩니다. 
 그럼 이번에는 복사 대입 연산자는 어떤지 알아 봅시다.
 역시 같은 에러가 나오고 있는걸 볼 수 있습니다. 이 경우도 마찬가지로 복사 대입 연산자는 지금 현재 있는 클래스의 멤버 변수만 대입을 해주고 있지, 기본 클래스의 멤버 변수의 대입은 안해주고 있기 때문입니다. 그래서 여기도 마찬가지로 기본 클래스의 복사 대입 연산자 호출을 해줍니다.

 객체의 복사 함수 작성시 확인해야할 사항이 두가지 있습니다. 첫번째, 해당 클래스의 멤버 변수를 모두 복사했는지? 두번째, 상속한 기본 클래스의 복사 함수도 호출해줘야 합니다. 그 외에도 Effective C++에서는 한가지 더 설명하고 있는 내용이 있는데, 이런 복사 함수를 살펴보면 중복된 코드들이 있습니다. 이 중복된 코드를 피하기 위해서 (예를 들어) 복사 대입 연산자에서 복사 생성자를 호출한다거나 이 반대로 하거나 하면 문제가 생깁니다. 그럼 실제로 Customer의 복사 대입 연산자 부분에 복사 생성자를 호출해 봅시다.


  복사 대입 연산자는 이미 객체가 완성된 상황에서 대입을 하기 때문에, 이 상황에서는 이미 생성자가 호출된 상태이기에 또 생성자를 호출하게 되는 형태를 가지게 됩니다. 이렇게 되면 멤버 변수의 값이 아까 처럼 쓰레기 값이 들어 가는 경우가 발생하므로 이렇게 사용해서는 안되고 이 반대의 경우도 마찬가지이다. 복사 생성자도 생성자 이기 때문입니다.

  그럼 이 중복된 코드를 어떻게 피할 수 있을까요? 제 3의 함수를 생성해서 호출해서 쓰는게 바로 답입니다. 

 * 객체 복사 함수는 주어진 객체의 모든 데이터 멤버 및 모든 기본 클래스 부분을 빠드리지 말고 복사해야 합니다. 
 * 클래스의 복사 함수 두 개를 구현할 때, 한쪽을 이요해서 다른 쪽을 구현하려는 시도는 절대로 하지 마세요. 그 대신, 공통된 동작을 제 3의 함수에다 분리해 놓고 양쪽에서 이것을 호출하게 만들어서 해결합시다.