방프리

17.07.11. Effective C++ 2. 생성자, 소멸자 및 대입 연산자 (항목12) 본문

C++/Effective C++

17.07.11. Effective C++ 2. 생성자, 소멸자 및 대입 연산자 (항목12)

방프리 2020. 1. 7. 05:22

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

 

일반적으로 클래스를 생성하면 생성자, 소멸자, 복사 생성자, 복사 대입 연산자

총 4가지의 함수가 기본적으로 생성이 됩니다. 

이 중 복사 생성자와 복사 대입 연산자는 합쳐서 복사 함수로 일컫는데 컴파일러가 기본적으로 생성해주지만

기본적으로 생성되는 복사 함수는 정말 문제가 많습니다.

왜일까요? 생성자와 소멸자는 아무 문제가 없는데 유독 복사 함수가 문제인 이유는

클래스는 컴파일러가 아닌 사용자가 제작했기 때문입니다.

그렇기 때문에 어떠한 행동이 맞는지 컴파일러 자신이 판단하기가

어려운 것이죠. 대충 복사 하라고 해서 복사를 했는데 그 과정에서 문제가 생겼다? 그 순간 총체적 난국이 생겨버립니다.

이쯤되면 "어떤 경우에 문제가 생기나요?" 라는 질문이 들어올법 합니다만...

다음 예제 코드를 살펴보겠습니다.

class Date { ... }

class Customer

{

public:

Customer() { }

~Customer() { }

Customer(const Customer& rhs)

:name(rhs.name)

{

printf("Customer copy constructor");

}

Customer& operator= (const Customer& rhs)

{

printf("Customer copy assignment constructor");

 

name = rhs.name;

 

return *this;

}

private:

string name;

Date date;

};

 

고객이라는 클래스와 날짜를 기록하는 클래스를 생성 후 고객 클래스의 멤버변수 타입으로 Date를 선언했습니다. 

자 그러면 복사 함수에서 어떤 식으로 동작하게 될까요? 우리의 기대는 name, date 모든 변수들이 복사될 것이라

기대하지만 컴파일러는 우리의 기대에 미치지 못합니다. 즉, name만 복사가 진행되고 date은 복사가 되지 않는 것이죠.

바로 부분복사가 이루어집니다. 더 억울한 것은 컴파일러 에러 조차 내뱉어주지 않는다는 것입니다.

여기만 끝이면 다행입니다. 하지만 이 문제는 객체지향의 요소 중 한 가지인 상속에서도 문제를 일으킵니다.

 

class PriorityCustomer : public Customer

{

public:

PriortyCustomer( const PriorityCustomer& rhs)

: priority(rhs.priority)

{

printf("PriorityCustomer copy constructor");

}

 

PriorityCustomer& operator=(const PriorityCustomer& rhs)

{

printf("PriorityCustomer copy assignment operator");

 

priority = rhs.priority;

 

return *this;

}

private:

int priorty;

};

 

이 경우엔 복사 함수가 실행된다면 부모클래스인 Customer 생성자만 불러와서는 초기화만 해주고

떠나버립니다. 다시말해 값 대입이고 머고 부모클래스의 데이터는 초기화 상태로 놔둬버린다는 것입니다.

하.... 정말 총체적 난국입니다. 이렇게 된다면 상속을 하는 의미도 없을 뿐더러 수 많은 디버그를 생산할 뿐입니다.

그럼 어떻게 해결해야할까요??

 

PriortyCustomer( const PriorityCustomer& rhs)

: Customer(rhs), priority(rhs.priority)

{

printf("PriorityCustomer copy constructor");

}

 

PriorityCustomer& operator=(const PriorityCustomer& rhs)

{

printf("PriorityCustomer copy assignment operator");

 

Customer::operator=(rhs);

priority = rhs.priority;

 

return *this;

}

자식 클래스의 복사 함수에서 부모클래스의 복사 함수를 호출하는 것입니다. 복사를 할 데이터의

부모 클래스 데이터까지 복사하겠다고 명시해주는 것이죠. 

 

이것만은 잊지말자!

* 객체 복사 함수는 주어진 객체의 모든 데이터 멤버 및 모든 기본 클래스 부분을 빠뜨리지 말고 복사해야 합니다.

* 클래스의 복사 함수 두 개를 구현할 때, 한족을 이용해서 다른 쪽을 구현하려는 시도는 절대로 하지 마세요. 그 대신, 공통된 동작을 제3의 함수에다 분리해놓고 양쪽에서 이것을 호출하게 만들어서 해결합시다.

Comments