06: 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자
클래스의 복사 생성자와 대입 연산자를 막고 싶다면 어떻게 해야 할까? 보통의 경우 특정한 기능을 지원하지 않았으면 하는 의도를 반영하는 방법은 그런 기능을 제공하는 함수를 선언하지 않는 것이다. 하지만 이 전략은 복사 생성자와 대입 연산자에 대해서는 불가하다. 왜냐하면 컴파일러가 자동적으로 선언해 버리기 때문이다.
해결 방법은 public 멤버로 두지 말고, 복사 생성자 및 대입 연산자를 private 멤버로 선언하는 것이다. 이렇게 하면 컴파일러가 자동적으로 기본 버전을 만들지 않고 외부로부터 호출할 수 없다.
하지만 멤버 함수나 friend 함수가 호출할 수 있다. 이것까지 막으려면, 정의를 하지 않으면 된다. 정의되지 않은 함수를 누군가가 호출하려 한다면 링크 시점에서 에러가 발생한다.
class HomeForSale
{
public:
...
private:
HomeForSale(const HomeForSale&);
HomeForSale operator=(const HomeForSale&);
};
이렇게 하면 객체의 복사가 시도될 때는 컴파일러에서 에러가 발생하고 멤버 함수나 friend 함수에서 호출되면 링커에서 에러가 발생한다.
추가적으로, 링크 시점 에러를 컴파일 시점 에러로 변경할 수 있다(에러 탐지가 미리 되서 더 좋다). 복사 생성자와 대입 연산자를 HomeForSale 자체에 넣지 말고 별도의 기본 클래스에 private 멤버로 넣고 이것으로부터 HomeForSale을 파생시키는 것이다.
class Uncopyable
{
protected:
Uncopyable() {} // 생성과 소멸 허용
~Uncopyable() {}
private:
Uncopyable(const Uncopyable&); // 복사는 방지
Uncopyable operator=(const Uncopyable&);
};
class HomeForSale : private Uncopyable
{
...
};
HomeForSale 객체의 복사를 외부에서 시도하려고 할 때, 컴파일러는 기본 클래스의 대응 버전을 호출하려고 할 것이다. 하지만 복사 함수들이 기본 클래스에서 공개되어 있지 않으므로 에러가 컴파일러에서 에러가 발생한다.
여기서 추가적으로, Uncopyable로부터의 상속은 public일 필요가 없다. 그리고 Uncopyable의 소멸자는 가상 소멸자가 아니어도 된다. 혹은 기본 클래스인 부스트 라이브러리의 noncopyable을 사용하는 것도 괜찮다.
이것만은 잊지 말자!
- 컴파일러에서 자동으로 제공하는 기능을 허용치 않으려면, 대응되는 멤버 함수를 private로 선언한 후에 구현은 하지 않은 채로 두자. Uncopyable과 비슷한 기본 클래스를 쓰는 것도 한 방법이다.
'C++ > Effective C++' 카테고리의 다른 글
[Effective C++] 08: 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 (1) | 2023.10.05 |
---|---|
[Effective C++] 07: 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 (0) | 2023.10.03 |
[Effective C++] 05: C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (1) | 2023.10.02 |
[Effective C++] 04: 객체를 사용하기 전에 반드시 그 객체를 초기화하자 (0) | 2023.09.30 |
[Effective C++] 03: 낌새만 보이면 const를 들이대 보자! (0) | 2023.09.28 |