본문 바로가기
개발 🛠💻/C++, Modern C++

C++ 특수 멤버 함수와 The rule of three five zero (3/5/0)

by 승지 T-Story 2022. 7. 25.

C++ 특수 멤버 함수 정의 규칙
자료 : https://en.cppreference.com/w/cpp/language/rule_of_three

 

The rule of three/five/zero - cppreference.com

[edit] Rule of three If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three. Because C++ copies and copy-assigns objects of user-defined types in va

en.cppreference.com


들어가기 앞서... 간단히 특수 멤버 함수란??

특수 멤버 함수

컴파일러가 자동 생성하는 멤버 함수로

생성자 / 소멸자
복사 생성자 / 복사 대입 연산자
이동 생성자 / 이동 대입 연산자

이렇게 총 6개의 특수 멤버 함수가 있다.


class Player
{
char * m_pName = nullptr ;
}

밑에서 다룰 다섯가지 특수 멤버 함수이다.
~Player () noexcept { } -- 소멸자
Player (const Player& rhs) { } -- 복사 생성자, const는 필수는 아니나 레퍼런스는 필수이다.
Player& operator= (const Player& rhs) { } -- 복사 대입 연산자
Player (Player&& rhs) noexcept { } -- 이동 생성자
Player& operator= (Player&& rhs) noexcept { } -- 이동 대입 연산자


 

특수 멤버 함수 자동 생성 규칙

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

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


Rule of Three

위의 class Player 같이 Raw pointer를 가지고 있는 class는 소멸자 / 복사 생성자 / 복사 대입 연산자를 정의해야 한다.
그리고 위의 특수 멤버 함수 자동 생성 규칙과 같이 소멸자 / 복사 생성자 / 복사 대입 연산자 중 하나라도 사용자가 직접 구현한다면, 나머지 둘도 모두 구현해야 한다.

깊은 복사

깊은 복사를 하지 않으면, 복사 생성/대입이 일어날 때, 같은 원본(메모리 주소)을 가리키게 되는 얕은 복사가 일어나고, 소멸자가 호출될 때, 이미 삭제된 메모리를 다시 삭제하게 되어 예외가 발생한다.
깊은 복사가 필요한 클래스라면 소멸자 / 복사 생성자 / 복사 대입 연산자 를 정의해준다.


 

 

Rule of Five

굳이 모든 멤버가 깊은 복사가 이루어질 필요 없이 객체의 데이터를 이동하는 방법을 사용할 수도 있어야 한다.
객체의 소유권을 이전하는 경우나, 일시적으로 생성되는 객체(임시 객체)가 필요한데 클래스의 크기가 크다면 깊은 복사를 할 때 비용이 많이 들게 되므로 얕은 복사를 해주어야 한다.

위의 class Player 같이 Raw pointer를 가지고 있고, 얕은 복사와 깊은 복사를 모두 사용한다면 class는 소멸자 / 복사 생성자 / 복사 대입 연산자 / 이동 생성자 / 이동 대입 연산자를 정의해야 한다.
그리고 위의 특수 멤버 함수 자동 생성 규칙과 같이 소멸자 / 복사 생성자 / 복사 대입 연산자 / 이동 생성자 / 이동 대입 연산자 중 하나라도 사용자가 직접 구현한다면, 나머지 넷도 모두 구현해야 한다.

클래스 정의 시 복사 생성자 / 복사 대입 연산자를 직접 정의하는 경우, 이동 생성자 / 이동 대입 연산자 는 컴파일러가 암묵적으로 생성하지 않는다. 따라서 해당 클래스에 이동 생성자 / 이동 대입 연산자 를 직접 정의하지 않으면 이동 불가능한 클래스가 된다.

얕은 복사

호출되고 바로 사라질 임시 객체 같은 경우에는 깊은 복사를 하는 연산 낭비를 할 필요가 없다.
또는 소유권 이전을 해줄때는 얕은 복사가 필요하다.
이럴 때 필요한 이동 생성자 / 이동 대입 연산자 를 정의한다.
임시 생성 객체의 Raw pointer 멤버 변수의 기존 객체 멤버 변수 주소 값으로 변경해주고, 기존 객체의 포인터는 null 처리해준다. ( std::move 를 적용해준다.)


 

Rule of Zero

위 다섯가지 특수 멤버 함수 (소멸자 / 복사 생성자 / 복사 대입 연산자 / 이동 생성자 / 이동 대입 연산자)들을 필요로 하지 않도록 Class를 설계한다.
동적 메모리 할당이 없는 Class.

 

 

 


생성자 복사생성자에 explicit 키워드 사용

변환생성자

암사적 변환에 의해 임시객체를 만들수도 있음(다른 예시)

 

복사생성자 묵시적 호출

 

댓글