티스토리 뷰
[Effective Modern C++] 항목 17. 특수 멤버 함수들의 자동 작성 조건을 숙지하라
pppgod 2019. 11. 2. 18:53C++ 에서 말하는 특수 멤버 함수란 C++ 이 스스로 작성하는 멤버 함수들을 가리킨다. 대표적으로 기본 생성자, 소멸자, 복사 생성자 등이 있다. 이 함수들이 생성되기 위한 가장 기본적인 조건은 클라이언트 코드에서 이 함수들을 사용할 때 생성된다. C++11 에 오면서 두 가지의 특수 멤버 함수가 추가됐다. 이 특수 멤버 함수의 조건을 알아보자.
이동 생성자와 이동 배정 연산자
C++ 의 가장 큰 특징이 바로 move semantics 이다. 이와 관련하여 이동 생성자와 이동 배정 연산자가 추가되었다. 이 둘은 아래와 같이 정의된다.
class Widget {
public:
...
Widget(Widget&& rhs);
Widget& operator=(Widget&& rhs);
...
};
두 특수 멤버 함수가 작성되는 경우에 이 둘의 역할은 클래스의 비정적 자료 멤버들과 기반 클래스에 대해 이동을 수행한다. 그러나 이 함수가 이동 연산이 실제로 일어난다는 보장은 없다. 이동이 활성화되지 않은 형식은 복사 연산들을 통해서 이동되기 때문이다. 더 자세한 내용은 항목 23에서 다룬다.
이동 연산들은 복사 연산들과 비슷하게 행동하지만 다른 부분이 존재한다.
복사 연산의 경우
상황
복사 생성자 선언
복사 배정 연산을 사용하는 클라이언트 코드 작성
결과
복사 배정 연산자 자동 생성
상황
복사 배정 연산자 선언
복사 생성자를 사용하는 클라이언트 코드 작성
결과
복사 생성자 자동 생성
이동 연산의 경우
상황
이동 생성자 선언
이동 배정 연산을 사용하는 클라이언트 코드 작성
결과
이동 배정 연산자를 생성하지 않음
원인
이동 생성자를 선언했다면 자동으로 생성하는 것이 적합하지 않을 것이라 예상
상황
이동 배정 연산자 선언
이동 생성자를 사용하는 클라이언트 코드 작성
결과
이동 생성자를 자동 생성하지 않음
원인
이동 배정 연산자를 선언했다면 자동으로 생성하는 것이 적합하지 않을 것이라 예상
그 외의 경우
상황
복사 연산 중 하나를 선언
이동 연산 사용하는 클라이언트 코드 작성
결과
이동 연산들이 작성되지 않음
원인
복사 연산을 선언했다면 멤버별 복사가 적합하지 않기 때문에 멤버별 이동 역시 적합하지 않을 것이라 예상
상황
이동 연산 중 하나를 선언
복사 연산 사용하는 클라이언트 코드 작성
결과
복사 연산들이 작성되지 않음
원인
이동 연산을 선언했다면 멤버별 이동이 적합하지 않기 때문에 멤버별 복사 역시 적합하지 않을 것이라 예상
상황
소멸자를 선언
복사 연산 및 이동 연산을 사용하는 클라이언트 코드 작성
결과
복사 연산과 이동 연산들이 작성되지 않음
원인
소멸자를 선언했다면 복사 연산과 이동 연산이 적합하지 않을 것이라 예상
문제가 생기는 경우들의 원인을 정리하면 다음과 같다.
프로그래머가 무언가를 직접 구현을 했다면, 기본적으로 생성하는 것이 불완전할 것이라 예상
위의 결과에 따라 이동 연산들이 자동으로 생성되기 위한 조건이다.
- 클래스에 그 어떤 복사 연산도 선언되어 있지 않다.
- 클래스에 그 어떤 이동 연산도 선언되어 있지 않다.
- 클래스에 소멸자가 선언되어 있지 않다.
default 연산자
처음 프로그램을 작성할 때, 컴파일러가 자동으로 생성하기를 기대하고 작성하였는데, 위에서 말한 문제로 어느 순간 컴파일러가 생성해주지 않는다면 의도치 않은 방향으로 프로그램이 동작할 수 있다. 위와 같은 문제를 피하는 방법은 default 연산을 사용하는 것이다. default 연산은 자동으로 생성되기를 원하는 상황에서 명시적으로 나타내는 방법이다. 사용법은 다음과 같다.
class Base {
public:
virtual ~Base() = default;
Base(Base&&) = default;
Base& operator=(Base&&) = default;
Base(const Base&) = default;
Base& operator=(const Base&) = default;
...
};
주의점
멤버 함수 템플릿이 존재하면 특수 멤버 함수의 자동 작성이 비활성화된다는 규칙은 없다.
class Widget {
public:
...
template<typename T>
Widget(const T& rhs);
template<typename T>
Widget& operator=(const T& rhs);
...
};
위와 같은 코드에서도 컴파일러는 복사 연산들과 이동 연산들을 작성한다. 이 점이 중요한 영향을 미칠 수 있는데 항목 26에서 설명하겠다.
참고 서적
스콧 마이어스, Effective Modern C++
'C++ > Effective Modern C++' 카테고리의 다른 글
[Effective Modern C++] 항목 19. 소유권 공유 자원의 관리에는 std::shared_ptr를 사용하라 (0) | 2019.11.10 |
---|---|
[Effective Modern C++] 항목 18. 소유권 독점 자원의 관리에는 std::unique_ptr를 사용하라 (0) | 2019.11.03 |
[Effective Modern C++] 항목 16. const 멤버 함수를 스레드에 안전하게 작성하라 (0) | 2019.10.30 |
[Effective Modern C++] 항목 15. 가능하면 항상 constexpr을 사용하라 (0) | 2019.10.29 |
[Effective Modern C++] 항목 14. 예외를 방출하지 않을 함수는 noexcept로 선언하라 (0) | 2019.10.20 |
- Total
- Today
- Yesterday
- Override
- auto
- C++14
- Effective
- const
- std::forward
- Modern
- 보편 참조
- Forwarding
- thread
- Perfect
- C
- CPP
- 보편참조
- C++11
- 람다
- MOVE
- C++
- std::move
- Future
- 포인터
- Unreal
- detach
- forward
- 발아시기
- Overloading
- async
- 다이소
- Effective Modern C++
- Join
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |