본문 바로가기

개발 Note/C++11,14 (modern C++)

effective modern c++: 멤버 함수 사용 제한을 delete 키워드로 하는 것이 낫다.

반응형

 

항목 11 :

 

우리가 class를 설계할때, 의도되지 않은 동작을 막기 위해 클라이언트에서 호출 할 수 없도록 막을 필요가 있습니다.

하지만, 특수 멤버 함수들인 C++이 필요에 따라 자동으로 작성된 멤버 함수들에서 이런 제한이 쉽지 않습니다.

특히 , 복사생성자, 대입 연산자등 몇몇에 대해서 private으로 정의하는 경우들이 있습니다.

 

class A{

public:

   int get() const;

   int set(int d);

private:

   A(const A& rhs); // unused ( or not defined)

   A& operator =(const A&); // unused (or not defined)

};

 

이에 관한 언급을 한 것이 11 항목입니다.

 

 

C++11에서는 이를 위한 더 나은 방법을 제공하는데요. 바로 delete 키워드입니다.

public:

  A(const A& rhs) = delete;

  A& operator =(const A* rhs) = delete;

 

이런 형식입니다. 앞으로 이런 형식을 삭제된 함수라고 표현 하겠습니다.

 

뭐가 더 나아졌지? 라는 의문을 가질 것입니다.

 

첫번째는 A(const A&rhs)를 사용 못하게 하기 위해서 선언했다는 표현을 주석으로 할 필요가 없어졌다는 것입니다.

만약, 주석이 없다면 오해의 소지도 약간 있죠.. 

 

두번째는 흥미로운 내용인데, 삭제된 함수는 private이 아니라 public으로 선언하는 것이 관례입니다.

이유는 , 클라이언트 코드가 멤버함수를 사용할때 먼저 접근성을 봅니다. 그리고 나서 삭제 여부를 확인합니다.

따라서 만약 삭제된 함수가 private이었다면, 먼저 private함수를 사용했다는 에러를 보이게 됩니다.

 

삭제된 함수는 접근 권한이 중요한 것이 아니라 의도적으로 삭제한 것이 더 중요한데도 말이죠.

그래서, 오류의 원인이 더 명확하게 밝혀지도록 public으로 선언하는 것입니다.

 

 

 

삭제된 함수의 두 번째 장점은, 어떤 함수에도 적용할 수 있습니다. 

private으로 제한 하는 방법은 class의 멤버 함수에만 사용할 수 있습니다.

 

예를 들어,

bool isLucky(int num); 라는 함수가 있다고 하면,

 

if( isLucky('a'))...

if( isLucky(true)) ...

if( isLucky(3.5)) ...

 

"의도된 코드인가" 라는 질문에는 '아니요' 가 정답이죠. 그러나 'a' 나 true 나 3.5나 모두 빌드되고 실행도 됩니다.

 

이를 엄격히 제한하고자 한다면,

bool isLucky(char) = delete;

bool isLucky(bool) = delete;

bool isLucky(double) = delete;  // double 과 float 을 배제

 

이렇게 되면, 'a' true 3.5f 모두 오류가 발생합니다.

 

 

삭제된 함수의 세 번째 장점은 

클래스 안의 함수 템플릿의 일부 인스턴스화를 방지하려는 목적으로 private을 이용하는 것은 불가능합니다.

아래 코드를 살펴보면 이해가 쉽습니다.

class Widget{

public:

 ...

  template<typaname T> 

  void processPointer(T* ptr)

   {...}

private:

   template<>

   void processPointer<void>(void*); // 오류가 발생함.

};

이 코드가 불가능한 이유는 템플릿 특수화는 반드시 클래스 범위가 아니라 이름공간(namespace) 범위에서 작성해야 한다는 것입니다.

따라서 다음과 같이 삭제된 함수를 사용해야 합니다.

 

class Widget{

public:

 ...

  template<typaname T> 

  void processPointer(T* ptr)

   {...}

};

 

template<>

void Widget::processPointer<void>(void*) = delete;

 

 

 

 

자신이 작성했던 코드를 delete로 바꾸고 컴파일 해봅시다.!!! ㅎ

 

 

[참조] effective modern c++ :항목 11