본문 바로가기

기타

C++ - 클래스 복사방지 Effective C++ 내용

항목 06: 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자


함수의 사용을 금해버리는 예제를 보도록 하자.

상황을 만들자면...
부동산 APP을 만드는데, 그 APP에서 매물로 내놓은 가옥을 하나의 클래스로 정의한다.
그리고 모든 자산은 세상에 하나 밖에 없다고 가정한다.

1: class HomeForSale { ... };


위와 같은 클래스를 정의하고
세상에 하나 밖에 없는 자산이니, 이 HomeForSale 클래스 객체에 대한
사본을 만드는 것은 이치에 맞지 않다.

1: HomeForSale h1;
2: HomeForSale h2;
3:
4: HomeForSale h3(h1); // h1을 복사하려 하나, 컴파일 되면 안됨!
5:
6: h1 = h2; // h2를 복사하려 하나, 컴파일 되면 안됨!


그러므로 앞서 말한 복사 생성자나 복사 대입 연산자를 쓸 수 없다.
컴파일러가 자동으로 만들어 주기도 하는 이것들을 쓸 수 없다는 말이다.

그렇다면 해결 방법은 무엇일까??

컴파일러가 자동적으로 생성하는 함수는 모두 public: 이 된다.
그러므로 우리는 컴파일러가 자동적으로 생성하는 함수들을 모두 private: 로 선언한다.

또 한가지 막아주어야 할 것이 있는데, 이는
friend를 이용해 private: 안의 함수들을 이용하는 것을 막아야 한다.
어떻게? 바로 함수에 '정의(define)'을 안하면 되는 것이다.

자, 예제를 보자.

1: Class HomeForSale {
2: public:
3: ...
4: private:
5: ...
6: HomeForSale(const HomeForSale&) // 선언만 달랑ㅋ
7: HomeForSale& operator=(const HomeForSale&);
8: };


만약, 이를 호출 하려 한다면 에러를 보는 것이므로 괜찮다. (어차피 호출하지 않을 것이므로)
매가변수가 없는게 걸리지만 이는 필수가 아니라 그냥 편하자고 하는 관례니 패스.
이제 이 함수를 호출하려 하면, 컴파일러가 걸러내 준다. :)

그리고 위의 소스에서 보는 것들은...
하나의 '기법'으로 굳어지기까지 했으니... 말이다.

한 가지 덧 붙이면,
링크 시점의 에러를 컴파일 시점으로 옮길 수도 있다. (이게 더 좋은 방법, 에러를 빨리 잡음)

복사생성자와 복사대입연산자를 private로 선언하되, 
이것을 HomeForSale 자체에 넣지 말고 별도의 기본 클래스에 넣고 
이것으로부터 HomeForSale을 파생시키는 것이다.
그리고 그 별도의 기본 클래스는 복사 방지만 맡으면 되는 것이다.

01: class Uncopyable {
02: protected: // 파생된 객체에 대해서
03: Uncopyable() {} // 생성과
04: ~Uncopyable() {} // 소멸을 허용함
05:
06: private:
07: Uncopyable(const Uncopyable&); // 하지만 복사는 방지함
08: Uncopyable& operator=(const Uncopyable&);
09: };
10:
11:
12: class HomeForSale: private Uncopyable { // 복사생성자도,
13: ... // 복사대입연산자도
14: }; // 이제 선언되지 않음


복사를 막고자 하는 HomeForSale 객체는 
Uncopyable로부터 상속받게 하고 그냥 두면 끝.

마지막으로,
Uncopyable의 구현과 사용법에 대해 기술적으로 미묘한 부분 몇가지를 지적하자.
 - Uncopyable로부터의 상속은 public일 필요가 없다. (항목 32, 39)
 - Uncopyable의 소멸자는 가상 소멸자가 아니어도 된다. (항목 7)



 *이것만은 잊지 말자!

  ** 컴파일러에서 자동으로 제공하는 기능을 허용치 않으려면, 
대응되는 멤버 함수를 private로 선언한 후에 구현은 하지 않은 채로 두십시오.
Uncopyable과 비슷한 기본 클래스를 쓰는 것도 한 방법입니다.