센로그
[EC++] 12. 객체의 모든 부분을 빠짐없이 복사하자 본문
커스텀 복사 생성자 및 복사 대입 연산자 구현 시 주의할 점
- 모든 데이터 멤버들을 빠짐없이 복사해야 한다.
- 빠진 요소가 있더라도 컴파일러가 친절하게 알려주지는 않는다.
예시 상황
1. 고객을 나타내는 Customer 클래스가 있다.
void logCall(const string& funcName);
class Customer {
public:
...
Customer(const Customer& rhs);
Customer& operator=(const Customer& rhs);
...
private:
string name;
};
Customer::Customer(const Customer& rhs) : name(rhs.name) {
logCall("Customer Copy Constructor");
}
Customer& Customer::operator=(const Customer& rhs) {
logCall("Customer Copy Assignment Operator");
name = rhs.name;
return *this;
}
- 커스텀 복사 생성자와 복사 대입연산자를 구현하였다.
- 문제 없이 잘 돌아간다.
2. 그러나 이때 데이터 멤버 하나를 더 추가한다면?
class Date { ... };
class Customer {
public:
...
private:
string name;
Date lastTransaction;
};
- 멤버가 추가되더라도 기존 복사 생성자와 복사 대입 연산자는 별도의 오류 없이 그대로 동작한다.
- 즉, 객체의 전체가 아닌 일부만 복사하는 바보 함수가 된다.
3. 가장 문제되는 상황은 상속에서의 상황이다.
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("Priority Customer Copy Consturctor");
}
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer* rhs) {
logCall("Priority Customer Copy Assignment Operator");
priority = rhs.priority;
return *this;
}
- Customer을 상속받는 PriorityCustomer 클래스가 있다.
- PriorityCustomer의 복사함수는 선언된 멤버들을 전부 복사하고 있는 것 같다.
- 그러나... Customer의 멤버함수는.. 복사가 되고 있지 않다!
해결 방안
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
:Customer(rhs), priority(rhs.priority) // 기본 클래스의 복사 생성자 호출
{
logCall("PriorityCustomer Copy Constructor");
}
PriorityCustomer& PriorityCustomer::operator= (const PriorityCustomer& rhs) {
logCall("PriorityCustomer Copy Assignment Operator");
Customer::operator=(rhs); // 기본 클래스 부분을 대입
priority = rhs.priority;
return *this;
}
- 파생 클래스의 복사 함수에서 기본 클래스의 복사 함수를 호출하게 만들자.
- 복사 생성자의 경우, 초기화 리스트에서 기본 클래스의 복사 생성자를 호출한다.
- 복사 대입 연산자의 경우, 함수 내부에서 기본 클래스의 복사 대입 연산자를 호출한다.
따라서, 객체의 복사 함수를 구현할 때는 다음 두가지를 꼭 확인한다.
- 해당 클래스의 데이터 멤버를 모두 복사한다.
- 이 클래스가 상속한 기본 클래스의 복사 함수를 호출한다.
그런데.. 복사 생성자랑 복사 대입 연산자는 내용이 비슷할텐데, 한쪽에서 다른 쪽을 호출하는 방식으로 쓰면 코드 중복도 피하고 좋지 않나?
- 안됨.
- 복사 생성자: 새로운 객체를 생성하고 다른 객체의 내용을 복사하여 초기화
- 복사 대입 연산자: 이미 생성된(초기화 완료된) 객체에 다른 객체의 내용을 복사
- 복사 대입 연산자에서 복사 생성자를 호출하는 경우
- 이미 객체가 생성되어 있는 상황에서 또다시 새로운 객체를 생성한다고? 말 안됨
- 복사 생성자에서 복사 대입 연산자를 호출하는 경우
- 대입 연산자는 이미 초기화가 끝난 객체에 값을 넣어주는 거임. 아직 초기화도 안 된 객체에 대해 대입이라고? 말 안됨
'Effective > Effective C++' 카테고리의 다른 글
[EC++] 13. 자원 관리에는 객체가 그만! (0) | 2024.11.18 |
---|---|
[EC++] 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2024.11.17 |
[EC++] 10. 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2024.11.17 |
[EC++] 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 (0) | 2024.11.17 |
[EC++] 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 (0) | 2024.11.16 |
Comments