설계가 잘 된 클래스들을 보면 객체를 복사하는 함수가 딱 둘만 있습니다.
복사생성자와 복사 대입 연산자 입니다. 이둘은 우리가 선언하지 않으면 컴파일러가 자동으로 만들어주는데, 깊은 복사가 되지 않습니다.
우리가 복사생성자와 복사 대입 연산자를 선언하여 깊은 복사를 해줘야 합니다.
이문제가 가장 크게 나타는 경우가 있는데, 예제를 보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class B : public A{ public: ... B(const B& rhs) : num(rhs.num) { //복사생성자 LOG("COPY constructor"); } B& operator=( const B& rhs){ //복사 대입 연산자 LOG("COPY operator"); num = rhs.num; return *this; } private: int num; }; |
위의 코드에 문제점은?
상속 관계에서 B의 복사생성자에는 기본클래스 A의 생성자에 넘길 인자들도 명시되어 있지 않아서 B객체의 A 부분은 인자 없이 실행되는 A 생성자, 즉 기본 생성자에 의해 초기화 됩니다.
위와 같은 코드는 아래와 같이 바꾸셔야 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class B : public A{ public: ... B(const B& rhs) : A(rhs), num(rhs.num) { //복사생성자 LOG("COPY constructor"); } B& operator=( const B& rhs){ //복사 대입 연산자 LOG("COPY operator"); A::operator=(rhs); num = rhs.num; return *this; } private: int num; }; |
우리는 위의 코드처럼 두가지를 꼭 확인해야합니다.
1. 해당클래스의 데이터 멤버를 모두 복사합니다.
2. 이 클래스가 상속한 기본클래스의 복사 함수도 꼬박꼬박 호출해 주도록 합시다.
그리고
복사생성자와 복사 대입 연산자의 코드 중복은 양쪽에서 겹치는 부분을 별도의 멤버 함수에 분리해 놓은 후에 이함수를 호출하게 만드는게 좋습니다. ( Private 멤버로 init......() )
절대 복사 대입 연산자에서나, 복사 생성자에서 서로를 호출해서는 안됩니다.
이것 만은 잊지 말자!
◆ 객체 복사 함수는 주어진 객체의 모든 데이터 멤버 및 모든 기본클래스 부분을 빠뜨리지 말고 복사해야 합니다.
◆ 클래스의 복사 함수 두개를 구현할 때, 한쪽을 이용해서 다른쪽을 구현하려는 시도는 절대로 하지 마세요, 그대신, 공통된 동작을 제3의 함수에다가 분리해 놓고 양쪽에서 이것을 호출하게 만들어서 해결 합시다.
'0x0001 > Effective C++' 카테고리의 다른 글
[Effective C++] 항목 14 : 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자 (0) | 2019.02.12 |
---|---|
[Effective C++] 항목 13 : 자원 관리에는 객체가 그만! (0) | 2019.02.12 |
[Effective C++] 항목 11 : operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2019.02.12 |
[Effective C++] 항목 10 : 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2019.02.11 |
[Effective C++] 항목 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 (0) | 2019.02.10 |