[Effective C++] 12. 객체의 모든 부분을 빠짐없이 복사하자

C++/Effective C++

2020. 7. 28. 22:24

1. 복사 함수

클래스 내의 복사 함수는 딱 둘이 존재한다.

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

캡슐화가 잘 된 훌륭한 클래스들은 이 둘만을 이용하여 복사를 이뤄낸다.

 

2. 기본적으로 생성된 복사 함수

클래스를 생성할 때 따로 작성을 안해줘도 자동으로 생성되는 함수들 중 하나가 복사 함수이다.

이 때 컴파일러는 클래스 내의 모든 데이터를 빠짐 없이 복사하게 된다.

 

3. 직접 생성한 복사 함수

기본적으로 생성된 복사 함수가 마음에 들지 않는 경우 (깊은 복사 얕은 복사 문제, 복사를 하고 싶지 않는 데이터가 있는 경우 등등..) 직접 복사 함수를 생성해야 한다. 하지만, 멤버 변수로 객체가 있는 경우 컴파일러는 해당 변수를 복사하지 않는다. 즉 완전 복사가 아니라 부분 복사가 일어난다.

 

따라서, 클래스에 데이터 멤버를 추가 했으면, 추가한 데이터 멤버를 처리하도록 복사 함수를 다시 처리할 수 밖에 없다.

 

4. 상속 받은 클래스에서 복사 함수

기본 클래스의 멤버 데이터도 모조리 복사를 해줘야 한다!

PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
: Customer(rhs),priority(rhs.priority)
{}

PriorityCustomer& PriorityCustomer::operator = (const PriorityCustomer& rhs)
{
    Customer::operator=(rhs);
    // ...
}

위의 Customer(rhs)를 통해 복사 생성을, Customer::operator=(rhs)를 통해 복사 대입 연산을 반드시 해준다!

 

객체의 복사 함수를 작성할 때는 다음의 두 가지를 꼭 확인하자!

  • 해당 클래스의 데이터 멤버를 모두 복사한다
  • 이 클래스가 상속한 기본 클래스의 복사 함수도 꼬박꼬박 호출해 주도록 한다

5. 복사 생성에서 복사 대입 연산을?

복사 대입 연산자에서 복사 생성자를 호출하는 것은 말이 안되는 발상이다. 이미 생성되어 존재하는 객체를 생성하는 것이기 때문이다. 역도 성립하지 않는다. 생성자의 목적은 객체 초기화인데 대입 연산의 역할은 '이미' 초기화가 끝난 객체에 값을 주는 것이기 때문이다.

 

복사 생성과 복사 대입 연산에 공통적인 코드가 존재하는 경우에는 이를 하나의 함수로 만들어 적용시켜 줄 수는 있다.

 

이것만은 잊지 말자!

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