본문 바로가기
Effective Modern C++

EMC++ Item 17: Understand special member function generation.

by gong재이 2022. 9. 12.
반응형

special member function

사용자가 작성하지 않아도 C++이 스스로 기꺼이 작성하는 멤버함수를 의미한다. 기본 생성자, 소멸자, 복사생성자, 복사 배정 연산자 등이 있다.

 

move constructor, move assignment operator

class Widget {
public:
    …
    Widget(Widget&& rhs); // move constructor
    Widget& operator=(Widget&& rhs); // move assignment operator
    …
};

이동 연산은 필요한 경우에만 만들어지고, 만약 생성되었다면 클래스의 비정적 멤버들에 대해서 "memberwise moves" 를 수행하게 된다. move constructor 는 클래스의 비정적 멤버들에 대해서 move-constructs 를 수행하고 move assignment operator 는 move-assigns 를 수행한다.

만약 이동 연산을 지원하지 않는다면 복사 연산이 수행되게 된다.

 

이동연산이 작성되는 조건

1. 이동연산은 독립적이지 않다.

move constructor 나 move assignment operator 중 하나를 선언하면 컴파일러는 다른 하나는 스스로 생성하지 않는다.
사용자가 이동 연산 중 하나를 선언하게 되었다면 컴파일러는 자동으로 생성되는 이동 연산이 현재 클래스에 맞지 않다고 판단한다.

 

2. 명시적으로 복사연산 또는 이동연산이 선언된 경우

복사연산(생성 또는 배정)을 하나라도 명시적으로 선언한 클래스에 대해서는 이동 연산을 스스로 만들지 않는다.

복사 연산을 새로 선언했다는 의미는 일반적인 객체 복사 방식이 클래스에 적합하지 않음을 의미한다.

이동연산을 하나라도 명시적으로 선언하게 되면 컴파일러는 복사 연산들의 자동 생성을 비활성화한다.

 

Rule of Three

만일 복사 생성자, 복사 배정연산자, 소멸자 중 하나라도 선언을 했다면 나머지 둘도 선언해야 한다는 지침이다.

 

이동연산들은 3가지 조건이 모두 만족될 때, 그리고 필요할 때에만, 자동으로 작성된다.

  1. 클래스에 그 어떤 복사 연산도 선언되어 있지 않다.
  2. 클래스에 그 어떤 이동 연산도 선언되어 있지 않다.
  3. 클래스에 소멸자가 선언되어 있지 않다.

 

=default

class Widget {
public:
    …
    ~Widget(); // user-declared dtor default copy ctor
    … 
    Widget(const Widget&) = default; // behavior is OK
    Widget& // default copy assign
    operator=(const Widget&) = default; // behavior is OK
    …
};

polymorphic base class 는 파생클래스 객체들을 조작하는데 쓰이는 인터페이스를 정의하는 클래스인데 이곳에 "=default" 를 유용하게 사용하는 경우가 많다.

소멸자를 가상으로 만드는 것 외에는 변경할 것이 없는, 즉 기본 구현이 적합한 경우가 많다. 

 

사용자가 직접 선언한 소멸자를 두면서 이동 연산을 지원하고 싶을 때도 "=default" 를 이용할 수 있다.

이동 + 복사 연산을 모두 지원하고 싶은 경우에는 복사 연산들에도 "=default" 를 선언하게 할 수 있다.

 

class Base {
public:
    virtual ~Base() = default; // make dtor virtual
    Base(Base&&) = default; // support moving
    Base& operator=(Base&&) = default;
    Base(const Base&) = default; // support copying
    Base& operator=(const Base&) = default;
    …
};

 

템플릿에서의 특수멤버

멤버함수 템플릿이 존재하면 특수 멤버 함수의 자동작성이 바활성화된다는 규칙은 없다.

class Widget {
    …
    template<typename T> // construct Widget
    Widget(const T& rhs); // from anything
    template<typename T> // assign Widget
    Widget& operator=(const T& rhs); // from anything
    …
};

컴파일러는 Widget의 조건이 만족된다면 복사연산들과 이동 연산들을 작성한다.

반응형

댓글