객체 복사 함수에는 복사 생성자와 복사 대입 연산자가 있다.
항목 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. 이 클래스가 상속한 기본 클래스의 복사 함수 호출
두 복사 함수(복사 생성자, 복사 대입 연산자)의 본문이 비슷하게 나오는 경우들도 종종 발생한다.
한 번쯤은 한쪽에서 다른 쪽을 호출하게 만들면 어떨까 라는 생각을 할 수도 있다.
해결법
양쪽에서 겹치는 부분을 별도의 멤버 함수로 분리하여 이 함수를 호출하자!