광고


[C++11] "default" and "delete" keyword TR1/C++11/C++14/C++17




0. 서문

들어가기에 앞서, 다음 링크는 default / delete에 대한 C++ 표준 문서이다.

C++ 클래스를 사용 하다보면, 컴파일러가 은근슬쩍 만들어내는 4 종류, 6 개의 함수가 있다.
이들을 C++ 표준에서는 "특수 멤버 함수"라 부른다.
  • 기본 생성자
  • 소멸자
  • 복사 연산자 (생성자 / 대입 연산자)
  • 이동 연산자 (생성자 / 대입 연산자)

각 종류별/함수별로 조금씩 다른 규칙들로 자동 작성이 되거나 금지된 함수로 생성된다.
default와 delete, 특히 default 키워드를 알아보기에 앞서 이 규칙들에 대한 이해가 필요하다.


1. 특수 멤버 함수 자동 작성 규칙

자동 작성되는 특수 멤버 함수들은 암묵적으로 공통적으로 아래의 속성들을 가진다.
  • 공개성 (public)
  • 인라인화 (inline)
  • 가상 소멸자가 아닌 경우 비가상 (파생 클래스의 가상 소멸자는 가상)

그리고 특수 멤버 함수들은 아래의 조건들이 모두 충족될 때만 자동 작성된다.
  • 함수 종류별로 개별 조건들 충족
  • 명시적으로 선언되어 있지 않는 상태
  • 호출되는 코드가 존재할 경우

아래부터는 특수 멤버 함수 종류별 조건들에 대해 살펴보도록 하자

1) 생성자

C++98에서부터 내려온 전통이 유지된다.
기본 생성자가 아닌, 다른 형태의 생성자가 존재할 경우 자동 작성되지 않는다.

2) 소멸자

C++98의 규칙과 거의 유사하나, C++11부터는 소멸자가 기본적으로 noexcept

3) 복사 생성자 / 복사 대입 연산자
  • 둘 중 하나라도 명시되어 있을 경우, 나머지 하나는 자동 작성되지 않는다.
  • 이동 연산자 (생성자 or 대입 연산자)가 하나라도 명시적으로 작성되어 있을 경우, 삭제(= delete) 된다.

4) 이동 생성자 / 이동 대입 연산자
  • 소멸자가 명시적으로 작성되어 있을 경우, 자동 작성되지 않는다.
  • 둘 중 하나라도 명시되어 있을 경우, 나머지 하나는 자동 작성되지 않는다. 
  • 복사 연산자 (생성자 or 대입 연산자)가 하나라도 명시적으로 작성되어 있을 경우, 자동 작성되지 않는다.

복사 연산자와 이동 연산자간 자동 작성 규칙의 차이점에 대해 유념하는 것이 좋다.
하나의 경우엔 삭제처리 되며, 하나의 경우엔 자동 작성만 되지 않을 뿐이다.

이 규칙들을 제대로 숙지하고 나면, "default" 키워드에 대한 필요성을 조금 더 이해할 수 있게 된다.


2. default : "기본 행동" 명시

특수 멤버 함수의 자동 작성 규칙에서 살펴 보았듯이, 각각이 자동 작성되는 규칙은 모두 다르다.
아래 예제를 보면, "default"에 대해 이해가 쉽게 갈 것이다.

  1. class Base
  2. {
  3. public:
  4.     // 소멸자 가상으로 명시적 선언
  5.     // 이동 연산자를 명시하지 않으면, 자동 생성되지 않는다.
  6.     virtual ~Base() = default;
  7.  
  8.     // 이제 이동 연산자 명시 선언
  9.     // 이동 연산자의 C++ 기본 행동으로 선언 (굳이 자체 이동 처리를 할 게 없다, 기본으로 충분)
  10.     // 이동 연산자가 선언되어 있으므로, 복사 연산자는 삭제된다.
  11.     Base(Base&&) = default;
  12.     Base& operator = (Base&&) = default;
  13.  
  14.     // 이제 복사 연산자까지 명시 선언
  15.     // 복사 연산자의 C++기본 행동으로 선언 (굳이 자체 복사 처리를 할 게 없다, 기본으로 충분) 
  16.     Base(const Base&) = default;
  17.     Base& operator = (const Base&) = default;
  18.  
  19.     // ... 하략 ...
  20. };

위 예제에서 6라인의 소멸자만 선언하였다면, 이동 연산자는 자동 작성되지 않는다.
따라서, 이동 연산을 요청하지만 실질적으로는 복사 연산을 수행하게 된다.
(이동 연산을 요청하였지만, 이동이 불가능할 경우엔 복사 연산을 시도한다)

이동 연산으로 충분한데 복사 연산이 발생할 경우 클래스의 자원 크기에 따라 엄청난 비용을 치를 수도 있다.
하여, 11~12라인에서 이동 연산자를 명시하였지만, Base 클래스 수준에서는 언어 기본 행동으로 선언해도 무방하다.
그렇기에 이동 연산자들을 모두 = default로 선언하였다.

그런데 이동 연산자가 하나 이상 명시적으로 선언되어 있고, 복사 연산자가 명시적으로 선언되어 있지 않으면, 복사 연산자들은 삭제(= delete)된다.
이를 방지하고 싶지만, 언어 기본 복사 연산으로도 충분하기에 복사 연산자들을 16~17라인에서 모두 = default로 선언하였다.

위 예제에서도 살펴 보았듯이, 
"default" 키워드의 핵심은 특수 멤버 함수의 행동 양식을 기본 행동으로 지정하는 것이다.


2. delete : 삭제된 함수

특정 클래스의 복사 연산자(생성 or 대입 연산)을 막고 싶은 경우, 기존 C++98 까진 아래와 같이 작성하였다.

  1. class NonCopyable
  2. {
  3. public:
  4.     NonCopyable() {}
  5.     ~NonCopyable() {}
  6.  
  7. private:
  8.     NonCopyable(const NonCopyable&);
  9.     NonCopyable& operator = (const NonCopyable&);
  10. };

위 방식은 우선 복사 생성자와 대입 연산자를 private으로 두어 외부에서 접근 에러가 발생하게 하는 방식이고,

만약, 위 코드에서 복사 생성자와 대입 연산자의 접근 지정자를 public으로 두었다면,
선언만 하고 정의를 하지 않아 링크 에러가 나게 하는 식으로 사용을 금지시키는 방식이었다.

C++11에서는 = delete를 붙여 삭제된 함수로 만들어 이들에 대한 사용을 막을 수 있다.

  1. class NonCopyable
  2. {
  3. public:
  4.     NonCopyable() {}
  5.     ~NonCopyable() {}
  6.  
  7.     // 명시적으로 복사 생성자 / 대입 연산자가 disable 처리 되었음
  8.     NonCopyable(const NonCopyable&) = delete;
  9.     NonCopyable& operator = (const NonCopyable&) = delete;
  10. };

C++11의 이러한 금지법은 C++98의 방식에 비해 다음 두 가지 측면에서 확실한 이점이 있다.
  • 접근 수준의 개념이 아닌, 확실한 삭제 개념이기에 훨씬 더 개념적으로 직관적이다.
  • 링크 타임 에러가 아닌 컴파일 타임 에러이므로, 파악하기가 쉽다.

또한, delete 키워드는 멤버 함수 외 비멤버 함수에서도 사용할 수 있기 때문에, 
아래 예제처럼 멤버 함수의 파라미터에 대해 암시적인 형변환이 일어나는 것을 사전에 방지할 수 있다.

  1. // int 타입에 대해선 받지 않겠음.
  2. struct NoInt
  3. {
  4.     void f(double i);
  5.     void f(int) = delete;
  6. };
  7.  
  8. // f의 인자로 double 형을 제외하고는 완전히 금지하겠다.
  9. // 즉, 컴파일러가 암시적으로 형 변환하는 것까지 완벽 차단.
  10. struct OnlyDouble
  11. {
  12.     void f(double d);
  13.     template<class T> void f(T) = delete;
  14. };

지금까지 default / delete 키워드에 대해 알아 보았지만, 아쉽게도 vs2013부터만 지원된다.


    덧글

    • 모카노 2015/06/02 20:24 # 삭제 답글

      C++11의 default에 대해서 많이 궁금했었는데 잘 알고 갑니다.
    • 요원009 2015/07/28 14:18 # 답글

      아하! 어디다 쓰나 고민이 많았는데... 감사합니다.
    • dewlit 2016/12/02 15:52 # 삭제 답글

      잘 보고갑니다. 감사합ㄴ다.
    댓글 입력 영역