Item 6: 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금하자

사본을 만드는 것을 막아야 하는 경우를 가정해 보자.

class HomeForSale { ... };

HomeForSale h1;
HomeForSale h2;

HomeForSale h3(h1); // 이렇게 복사하면 안됨.
h1 = h2;            // 이렇게도 복사하면 안됨.

위 코드에서 HomeForSale을 선언할 때 복사생성자나 복사대입연산자를 사용자가 직접 정의하지 않았다고 하더라도 컴파일러는 HomeForSale h3(h1)와 h1 = h2 와 같은 코드를 보고 자동으로 이들을 생성해 버린다.

컴파일러가 자동으로 이 함수들을 만드는 것을 막는 방법은 사용자가 이 함수들을 직접 만드는 것이다. 또한 클래스 밖에서 이들이 호출되지 않게 하기 위해 public 이 아닌 것으로 선언하는 것이다.

이렇게 한다고 해도 다른 멤버함수에서 호출이 가능한 상황이다. 이를 해결하는 방법으로는 선언만 하고 정의를 하지 않는 것이다.

해결 방법

  • private으로 선언만하고
  • 정의를 하지 말자.
class HomeForSale {
public:
    ...
private:
    ...
    HomeForSale(const HomeForSale&);            // 선언만 존재
    HomeForSale& operaotr=(const HomeForSale&); // 마찬가지

위 경우는 에러가 링크 시점에 나온다. 하지만 에러는 뒤로 미룰수록 좋지 않다. 이를 해결해 보자.

해결 방법

  • 복사를 방지하는 전담 클래스를 만들자.
class Uncopyalble {
protected:             // 파생 클래스에 대해
    Uncopyable() {}    // 생성과 소멸을
    ~Uncopyable() {}   // 허용한다
private:
    Uncopyable(const Uncopyable&);           // 하지만 복사는
    Uncopyable& operator(const Uncopyable&); // 허용하지 않는다.

복사를 막고 싶은 객체는 이렇게 사용하자.

class HomeForSale: private Uncopyable { // 복사생성자도,
    ...                                 // 복사대입연산자도
};                                      // 이제는 선언되지 않는다.

만약 누군가가 이 객체를 복사하려고 하면 컴파일러는 복사생성자나 복사대입연산자를 자도으로 만들려고 시도할 것이고 이 과정에서 베이스클래스의 복사생성자와 복사대입연산자를 호출하려고 할 것이다. 이때 컴파일 에러가 발생한다.

  • public 상속일 필요가 없다 (항목 32, 39).
  • Uncopyable은 가상 소멸자가 아니어도 된다.(항목 7)
  • 공백 기본클래스 최적화가 적용 가능하다.

  • 다중 상속의 가능이 있다.(항목 40) ㅡㅡ;

  • 다중 상속시 공백 기본클래스 최적화 적용이 안됨.

정리

  • 컴파일러에서 자동으로 제공하는 기능을 허용치 않으러면, 대응되는 멤버함수를 private으로 선언한 후에 구현은 하지 말자.
  • Uncopyable 클래스를 쓰는 것도 좋다.

results matching ""

    No results matching ""