본문 바로가기

Effective C++ 정리

06. 컴파일러가 자동으로 생성하는 함수 호출 없애기

private 접근 지정자 활용하기

// Dog.h //

// 비어있는 클래스
class Dog {...}

 

// main.cpp //

#include "Dog.h"

int main()
{
    Dog dog1;
    Dog dog2(dog1); // 기본 복사 생성자 호출

    dog1 = dog2; // 기본 복사 대입 연산자 호출
  
    return 0;
}

 

사용자가 복사 생성자와 복사 대입 연산자를 따로 정의하지않으면

컴파일러가 자동으로 생성해주기 때문에 호출할 수 있게 됩니다.

이를 막고 싶을 때 함수의 접근 지정자 private을 활용합니다.

 

// Dog.h //

class Dog
{
  public:
  {...}
  
  private:
    Dog(const CDog&); // 선언만 한다.
    Dog& operator = (const Dog&); // 선언 시 매개변수의 이름은 없어도 된다.
};

 

// main.cpp //

#include "Dog.h"

int main()
{
  Dog dog1;
  // CDog dog2(dog1); // 컴파일 에러 발생 !!

  // dog1 = dog2; // 컴파일 에러 발생 !!
  
  return 0;
}

 

복사 생성자와 복사 대입 연산자가 외부에서 호출되는 것을 막을 수 있게 되었습니다.

 

// Dog.h //

class Dog
{
  // friend 지정
  private:
      friend class Friend;

  public:
  {...}
  
  private:
    Dog& operator = (const CDog&);
    
  private:
    void Func()
    {
       CDog dog1;
       CDog dog2;
       
       dog2 = dog1; // 컴파일 허용
    }
};

 

// Friend.h //

#include "Dog.h"

class Friend
{
  private:
    void Func()
    {
       Dog dog1;
       Dog dog2;
       
       dog2 = dog1; // 컴파일 허용
    }
};

 

하지만 friend 함수로 인해 확실하게 막을 수 없게 되었네요.

이럴 땐 별도의 파생 클래스를 생성해서 확실하게 막을 수 있습니다.

 

// Dog.h //

class Uncopyable 
{ 
  // 파생된 객체의 생성과 소멸을 허용한다. 
  protected: 
    Uncopyable() 
    { 
    } 
     
    ~Uncopyable() 
    { 
    } 
     
  private: 
    // 하지만 복사 대입 연산자를 방지한다. 
    Uncopyable& operator = (const Uncopyable&); 
};

// 별도로 생성한 파생 클래스로부터 private 상속
class Dog  :  private Uncopyable
{
  private:
      friend class Friend;

  public:
  {...}
  
  private:
   // 복사 대입 연산자를 파생된 클래스에 선언한다.
   // Dog& operator = (const Dog&);
    
  private:
    void Func()
    {
       Dog dog1;
       Dog dog2;
       
       // dog2 = dog1; // 컴파일 에러 발생 !!
    }
};

 

// Friend.h //

#include "Dog.h"

class Friend
{
  private:
    void Func()
    {
       Dog dog1;
       Dog dog2;
       
       // dog2 = dog1; // 컴파일 에러 발생 !!
    }
};

 

확실하게 복사 대입 연산자가 외부에서 호출 되는 것을 막을 수 있게 되었습니다.

실제로 이와 같은 방법을 제공하는 부스트 라이브러리의 noncopyable 클래스가 존재한다는군요.