728x90
반응형

특수 멤버 함수

컴파일러가 스스로 작성할 수 있는 멤버 함수들(기본 생성자, 소멸자, 복사 연산자들, 이동 연산자들)을 가리켜 특수 멤버 함수라고 부른다.

이 함수들은 클래스에 명시적으로 선언되어 있지는 않지만 이 함수들을 사용하는 클라이언트 코드가 존재할 때에만 생성된다.

작성된 특수 멤버 함수들은 암묵적으로 public이며 inline이다. 그리고 가상 소멸자가 있는 base class를 상속하는 파생 클래스의 소멸자를 제외하고는 non-virtual이다. 가상 소멸자가 있는 base class를 상속하는 경우 파생 클래스의 소멸자는 virtual로 선언된다.

그리고 C++11에 오면서 두 가지의 특수 멤버 함수가 추가되었는데, 이 특수 멤버 함수의 조건을 알아보자.

 

이동 생성자와 이동 배정 연산자

class Widget
{
public:
  ...
  Widget(Widget&& rhs); // 이동 연산자
  Widget& operator=(Widget&& rhs); // 이동 배정 연산자
  ...
};

기본적으로 이동 생성자는 주어진 매개변수 rhs의 비정적 자료 멤버 각각을 이용해 클래스의 해당 자료 멤버들을 각각 이동 생성하고, 이동 배정 연산자는 주어진 매개변수 rhs의 비정적 자료 멤버 각각을 클래스의 해당 자료 멤버들에 각각 이동 배정한다. 그러나 이 함수가 이동 연산이 실제로 일어난다는 보장은 없다. 이동이 활성화되지 않은 형식은 복사 연산들을 통해서 이동되기 때문이다.

이동 연산은 복사 연산들과 비슷하게 행동하지만 다른 부분이 존재한다.

 

복사 연산의 경우

복사 생성자 선언
복사 배정 연산을 사용하는 클라이언트 코드 작성

결과
복사 배정 연산자 자동 생성

---
복사 배정 연산자 선언
복사 생성자를 사용하는 클라이언트 코드 작성

결과
복사 생성자 자동 생성

 

이동 연산의 경우

이동 생성자 선언
이동 배정 연산을 사용하는 클라이언트 코드 작성

결과
이동 배정 연산자를 생성하지 않음

원인
이동 생성자를 선언했다면 자동으로 생성하는 것이 적합하지 않을 것이라 예상

---
이동 배정 연산자 선언
이동 생성자를 사용하는 클라이언트 코드 작성

결과
이동 생성자를 자동 생성하지 않음

원인
이동 배정 연산자를 선언했다면 자동으로 생성하는 것이 적합하지 않은 것이라 예상

 

그 외의 경우

복사 연산 중 하나를 선언
이동 연산 사용하는 클라이언트 코드 작성

결과
이동 연산들이 작성되지 않음

원인
복사 연산을 선언했다면 멤버별 복사가 적합하지 않기 때문에 멤버별 이동 역시 적합하지 않을 것이라 예상

---
이동 연산 중 하나를 선언
복사 연산 사용하는 클라이언트 코드 작성

결과
복사 연산들이 작성되지 않음

원인
이동 연산을 선언했다면 멤버별 이동이 적합하지 않기 때문에 멤버별 복사 역시 적합하지 않을 것이라 예상

---
소멸자를 선언
복사 연산 및 이동 연산을 사용하는 클라이언트 코드 작성

결과
복사 연산과 이동 연산들이 작성되지 않음

원인
소멸자를 선언했다면 복사 연산과 이동 연산이 적합하지 않을 것이라 예상

프로그래머가 직접 무언가를 구현했을 때, 기본적으로 생성하는 것이 불완전할 것이라 예상하기 때문에 문제가 생긴다.

위의 결과에 따라 이동 연산들이 자동으로 생성되기 위한 조건은 아래와 같다.

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

 

default 연산자

C++11에서는 복사 연산이나 소멸자를 선언하는 클래스에 대한 복사 연산들의 자동 작성이 비권장 기능으로 분류되었다.

하지만 컴파일러가 작성한 함수들의 행동이 정확하다면 default연산자를 사용해 명시적으로 기본 행동을 사용하겠다고 표현할 수 있다.

즉, default연산은 자동으로 생성되기를 원하는 상황에서 명시적으로 나타내는 방법이다.

class Widget
{
public:
    ...
    ~Widget();
    ...
    Widget(const Widget&) = default; // 기본 복사 생성자 사용
    Widget& operator=(const Widget&) = default; // 기본 복사 배정 사용
    ...
};

하나의 이동/복사 연산을 명시적으로 작성한 경우 각 연산들에 =default를 사용함으로써 기존 연산들을 계속 사용할 수 있다.

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

class Widget
{
public:
    ...
    template<typename T>
    Widget(const T& rhs);
    
    template<typename T>
    Widget& operator=(const T& rhs);
    ...
};

위와 같은 코드에서도 컴파일러는 복사 연산들과 이동 연산들을 작성한다. 이 점이 중요한 영향을 미칠 수 있는데 항목 26에서 설명한다.

 

특수 멤버를 관장하는 C++11의 규칙들

기본 생성자

C++98의 규칙들과 같다. 클래스의 명시적 선언 생성자가 없는 경우 자동 생성된다.

 

소멸자

C++98과 같다. 유일한 차이는 소멸자가 기본적으로 noexcept인 점이다.

 

복사 생성자

실행 시점 행동은 C++98과 같다. 비 정적 자료 멤버들을 멤버별로 복사 생성한다. 명시적으로 생성되지 않았을 때만 자동 생성되며 명시적으로 생성된 복사 배정 연산자나 소멸자가 있는 클래스에서는 자동 작성되는 기능이 비 권장된다.

 

복사 배정 연산자

실행 시점 행동은 C++98과 같다. 비 정적 자료 멤버들을 멤버별로 복사 배정하며, 명시적으로 생성되지 않았을 때만 자동 생성, 명시적으로 생성된 복사 생성자나 소멸자가 있는 클래스에는 자동 작성이 비 권장된다.

 

이동 생성자와 이동 배정 연산자

비정적 자료 멤버의 멤버별 이동 수행한다. 명시적으로 선언된 복사 연산과 이동 연산, 소멸자가 없을 때 자동으로 생성된다.

 

요약

  • 컴파일러가 스스로 작성할 수 있는 멤버 함수들, 즉 기본 생성자, 소멸자, 복사 연산들, 이동 연산들을 가리켜 특수 멤버 함수라고 부른다.
  • 이동 연산들은 이동 연산들이나 복사 연산들, 소멸자가 명시적으로 선언되어 있지 않은 클래스에 대해서만 자동으로 작성된다.
  • 복사 생성자는 복사 생성자가 명시적으로 선언되어 있지 않은 클래스에 대해서만 자동 작성되며, 만일 이동 연산이 하나라도 선언되어 있으면 삭제된다. 복사 배정 연산자는 복사 배정 연산자가 명시적으로 선언되어 있지 않은 클래스에 대해서만 자동 작성되며, 만일 이동 연산이 하나라도 선언되어 있으면 삭제된다. 소멸자가 명시적으로 선언된 클래스에서 복사 연산들이 자동 작성되는 기능은 비권장이다.
  • 멤버 함수 템플릿 때문에 특수 멤버 함수의 자동 작성이 금지되는 경우는 전혀 없다.
728x90
반응형