객체 복사 함수에는 복사 생성자와 복사 대입 연산자가 있다.
항목 5에서 보았듯이 이러한 객체 복사 함수는 필요할 때 컴파일러가 자동으로 생성하지만, 사용자가 직접 구현할 수도 있다.
void logCall(const std::string& funcName); // 로그 출력 함수
class Customer {
public:
...
Customer(const Customer& rhs);
Customer& operator=(const Customer& rhs);
...
private:
std::string name;
};
Customer::Customer(const Customer& rhs):name(rhs.name)
{
logCall("Customer copy constructor");
}
Customer& Customer::operator=(const Customer& rhs)
{
logCall("Customer copy assignment constructor");
name=rhs.name; // rhs의 데이터 복사
return *this; // 항목 10
}
고객을 나타내는 클래스를 생성했다.
복사 함수를 호출할 때마다 로그를 출력하도록 하였다.
class Date { ... };
class Customer {
public:
...
private:
std::string name;
Date lastTransation;
};
데이터 멤버를 하나 추가했는데 복사 함수는 그대로라면 복사 함수의 동작은 완전 복사가 아니라 부분 복사가 된다. (name만 복사, lastTransaction은 복사X)
그럼에도 컴파일러는 어떤 경고도 보내지 않는다.
결국 사용자는 클래스에 데이터 멤버를 추가하면 복사 생성자, 복사 대입연산자, 모든 생성자를 갱신해야 한다.
이러한 문제는 클래스 상속에서 더욱 두드러진다.
// Customer 클래스를 상속
class PriorityCustomer: public Customer {
public:
...
PriorityCustomer(const PriorityCustomer& rhs);
PriorityCustomer& operator=(const PriorityCustomer& rhs);
...
private:
int priority;
};
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs):priority(rhs.priority)
{
logCall("PrioirtyCustomer copy constructor");
}
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
logCall("PrioirtyCustomer copy assignment constructor");
priority=rhs.priority;
return *this;
}
언뜻 보았을 땐 문제가 없어보인다. 객체 복사 함수에서 PriorityCustomer의 멤버 변수 priority를 복사하고 있기 때문이다.
그러나 Customer로부터 상속한 데이터 멤버들은 복사가 안되고 있다!
Customer 클래스에 넘길 인자들조차 명시XCustomer의 데이터 멤버들은 인자 없이 실행되는 Customer 생성자(=기본 생성자)에 의해 초기화Customer 데이터 멤버들에 대한 대입X// 기본 클래스의 복사 생성자 호출
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs):Customer(rhs), priority(rhs.priority)
{
logCall("PrioirtyCustomer copy constructor");
}
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
logCall("PrioirtyCustomer copy assignment constructor");
Customer::operator=(rhs); // 기본 클래스의 복사 대입 연산자 호출
priority=rhs.priority;
return *this;
}
파생 클래스의 복사 함수에서 기본 클래스의 복사 함수를 호출하여 해결한다.
객체 복사 함수 작성법
1. 해당 클래스의 데이터 멤버를 모두 복사
2. 이 클래스가 상속한 기본 클래스의 복사 함수 호출
두 복사 함수(복사 생성자, 복사 대입 연산자)의 본문이 비슷하게 나오는 경우들도 종종 발생한다.
한 번쯤은 한쪽에서 다른 쪽을 호출하게 만들면 어떨까 라는 생각을 할 수도 있다.
해결법
양쪽에서 겹치는 부분을 별도의 멤버 함수로 분리하여 이 함수를 호출하자!