728x90
반응형

복사 생성자와 복사 대입 연산자를 사용하고 싶지 않을 때

항목 5에서 컴파일러가 은근슬쩍 만들어내는 함수들에 대해 알았으니, 이러한 함수들을 사용하고 싶지 않을 때에 대해서 알아보자.

첫 번째 해결의 열쇠는 다음과 같다. 컴파일러가 생성하는 함수들은 모두 public멤버가 되지만, 직접 선언할 때는 public멤버로 선언해야 한다고 요구하는 부분은 없다는 점이다.

즉, 복사 생성자 및 복사 대입 연산자를 private 멤버로 선언하면

  • 클래스 멤버 함수가 명시적으로 선언되기 때문에 컴파일러는 자신의 기본 버전을 만들 수 없음
  • private의 접근성을 가지므로 외부로부터 호출도 불가능하여 복사를 할 수 없음

두 가지 이유로 인해 외부에서 복사는 불가능하게 할 수 있다.

 

하지만 private멤버 함수는 그 클래스의 멤버 함수 및 friend 함수가 호출할 수 있다는 점에서 허점이 존재하는데, 이것은 정의를 안 하는 방법으로 해결할 수 있다.

정의를 안 하는 방법은 아래와 같은데,

class HomeForSale
{
public:
	...
private:
	HomeForSale(const HomeForSale&); // 선언
	HomeForSale& operator=(const HomeForSale&);
}

이제 외부에서 복사 생성자 및 복사 대입 연산자를 호출한다면(클래스 내부나 friend함수 외엔 호출도 못하겠지만), 정의되지 않은 함수를 호출하는 것이므로 링크 시점에 에러를 발생시킬 것이다.

 

하나 더 덧붙이자면, 에러 탐지는 미리 하는 것이 좋으므로 링크 시점 에러를 컴파일 시점 에러로 옮겨보자.

방법은 간단하다.

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

class Uncopyable
{
protected:
	Uncopyable();
	~Uncopyable();
    
private:
	Uncopyable(const Uncopyable&);
	Uncopyable& operator=(const Uncopyable&);
}

class HomeForSale: private Uncopyable
{
}

이렇게 Uncopyable을 상속받은 HomeForSale 클래스는 이제 복사 생성자도 복사 대입 연산자도 선언되지 않는다.

 

Uncopyable의 구현과 사용법

  • Uncopyable로부터 상속은 public일 필요성이 없다.
  • Uncopyable의 소멸자는 가상 소멸자가 아니어도 된다.
  • Uncopyable클래스는 데이터 멤버가 전혀 없기 때문에 공백 기본 클래스 최적화 기법이 먹혀 들어갈 여지도 있다.
    • 하지만 Uncopyable 클래스는 기본 클래스이기 때문에 이 기법을 사용 시 다중 상속으로 갈 가능 성도 있음
    • 다중 상속 시 공백 기본 클래스 최적화가 돌아가지 못할 때가 있음
  • 부스트 라이브러리를 보면 Uncopyable과 똑같은 구실을 하는 클래스가 있는데 이것을 써도 된다. (noncopyable 클래스)

 

요약

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