[effective C++] Chapter 02 : 06. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자
06. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자
- 복사 생성자 / 복사 대입 연산자 private 선언
- 클래스 멤버 및 friend 함수의 호출 제한
- 복사 방지를 의미하는 클래스 정의
- 이것만은 일지 말자 !
복사 생성자 / 복사 대입 연산자 private 선언
만약 하나의 객체가 복사되는 것을 막고 싶다면 어떻게 하는 것이 좋을까 ? 예를 들어, HomeForSale이라는 클래스가 있고 이는 자산을 의미하며 자산은 유일하기 때문에 이것의 사본을 만드는 것은 이치에 맞지 않다고 한다면, 해당 객체를 복사하려 하는 코드는 컴파일 되지 않게 만드는 것이 맞다.
해답은 컴파일러가 자동으로 생성하는 모든 함수는 public으로 처리 된다는 사실에 있다. 따라서 자동으로 생성되는 복사 생성자와 복사 대입 연산자 또한 public으로 선언이 되고 우리는 자동으로 생성되는 것을 방지하기 위해 직접 선언을 해주어야 한다. 여기서 이들 함수를 public으로 선언해야 한다는 규약은 어디에도 없다. 복사 생성자 및 복사 대입 연산자를 private 멤버로 선언 한다면 일단 멤버 함수가 명시적으로 선언되기 때문에 컴파일러는 자동 생성을 할 수 없게 되고 private의 특성 상 외부로부터의 호출을 차단할 수 있게 된다.
클래스 멤버 및 friend 함수의 호출 제한
위의 복사 생성자 및 복사 대입 연산자의 외부 호출을 차단하는 방법은 완전하지 않다. private 멤버 함수는 동일한 클래스 및 friend 함수가 호출 할 수 있다는 위험성이 있다. 이것까지 방지하기 위해서는 정의를 하지 않으면 된다. 정의되지 않은 함수를 호출하려고 한다면 링크되는 시점에 에러가 발생하기 때문에 복사하는 것을 방지할 수 있는 일종의 꼼수로 사용될 수 있다. 즉, 객체의 사본을 만드는 것을 방지하기 위해서 복사 생성자와 복사 대입 연산자를 private으로 선언하되 정의를 하지 않는 것은 하나의 기법으로 활용할 수 있다.
위 코드는 앞서 말한 기법의 사용 예이다. 선언부에 매개변수 이름이 존재하지 않지만 이들은 구현될 에정이 없고 사용될 예정도 없기 때문에 문제가 없다. 만약 HomeForSale 객체의 복사를 시도하게 된다면 에러가 발생된다.
복사 방지를 의미하는 클래스 정의
앞서 말한 일종의 '기법'은 링크 시점에서 에러를 일으켜 복사를 방지하는 방법이다. 하지만 에러의 탐지는 빠르면 빠를수록 좋고, 같은 기법을 유지하면서 링크 시점의 에러를 컴파일 시점의 에러로 옮길 수 있는 방법이 있다. 기존과 동일하게 복사 생성자와 복사 대입 연산자를 private으로 선언하되, 이를 복사를 방지하고 싶은 클래스가 아닌 별도의 기본 클래스에 명시하고 복사를 방지하고 싶은 클래스는 해당 클래스를 상속 받게 구성하는 방법이다.
UnCopyable 클래스는 생성과 소멸은 허용하고 복사와 복사 대입은 방지하게 정의 되어있다. 그리고 기존의 HomeForSale 클래스는 이를 상속 받는다. 위의 구조로 코드를 작성하면 HomeForsale 객체의 복사를 외부, 멤버 함수, 프렌드 함수에서 호출 시도하는 경우 에러를 발생시켜서 해당 동작을 방지할 수 있다.
이것은 잊지 말자 !
컴파일러에서 자동으로 제공하는 기능을 허용치 않으려면, 대응되는 멤버 함수를 private으로 선언한 후에 구현은 하지 않은채로 둬라. 기본-파생 클래스를 쓰는 것도 하나의 방법이다.