Rule of 0/3/5 의 의도를 이해하자

2023. 11. 20. 00:34배움엔 끝이없다/C++

Rule of 3, 5, 0라는 표현을 많이 들어봤지만, 제대로 이해하지 않고 실제로 활용하지도 않고 있었다.

cpp reference에도 관련 항목이 있다.

 

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

배경

C++에서 클래스를 만들면, 생성자에서 자원을 할당하고 소멸자에서 자원을 해제하는 경우가 많이 생긴다.

이럴 경우 컴파일러가 자동으로 생성해주는 생성자/소멸자를 사용할 수 없어 직접 정의해준다.

만약 객체가 자원을 할당하고 해제한다면, 객체 복사시에는 deep copy를, rvalue에 대한 복사시에는 move를 해야할 것이다.

그렇기 때문에 이런 경우 복사생성자, 대입연산자, rvalue 복사생성자, rvalue 대입연산자를 직접 작성해줘야한다.

Rule of 3

Rule of 3란, 복사생성자/대입연산자/소멸자 중 하나라도 직접 정의했다면 나머지를 모두 정의(혹은 삭제)하라는 규칙이다.

사용자 정의 소멸자를 썼다는 건 자원 관리를 한 다는 것이고, 그렇다면 default 복사생성자/대입 연산자가 제대로 작동하지 않을 것이기 때문이다.

Rule of 5

Rule of 5는 move semantics가 추가된 C++11부터 생긴 Rule of 3의 확장으로,

rvalue를 받는 생성자와 rvalue 대입연산자도 추가로 구현하라는 뜻이다.

하지 않을 경우 복사 과정에서 성능 손해를 볼 확률이 높기 때문이다.

왜?

기본생성자와 함께 Big5 { T(const T&), T(T&&), T& operator=(const T&), T& operator=(T&&), ~T() }는

따로 명시하지 않으면 컴파일러가 자동 생성한다.

인자가 있는 생성자를 만들면 기본생성자를 생성하지 않는 규칙과 비슷하게,

사용자가 소멸자를 만들면 이동생성자와 이동대입연산자를 컴파일러가 만들어주지 않는다.

https://blog.feabhas.com/2015/11/becoming-a-rule-of-zero-hero/

 

이런 규칙은 뭐를 정의했냐에 따라 다른데, 이걸 외우고 있기 힘들고 API의 사용자가 이를 외우고 있는지 알 수 없으므로,

전부 명시하자는 의도이다.

그리고 대부분 자원 관리를 한다면 직접 구현해야할 경우가 대부분일 것이다.

Rule of 0

자원 관리를 스스로 하지 말라는 것이다. (스마트 포인터 등 RAII 객체를 사용하라)

그러면 소멸자를 직접 구현하지 않아도 되고, 그러면 다른 Big5를 명시적으로 구현하지 않아도 RAII 등에 의해 자원이 잘 해제 될 것이고 default Big5로도 충분하다.

ramensoup 프로젝트에서 모든 코드에 이 규칙을 적용하는 리팩토링 작업을 했었다.

하면서 여러 케이스에 대한 Rule of 5 적용법을 찾아봤다.

보너스 : 부모 클래스의 Rule of 5? 0?

복사를 허용하고 싶지 않다면 명시적으로 Big4를 삭제 (=delete;) 해주면 된다.

다형성을 활용할 가상 소멸자를 구현한 부모클래스는 Big4를 삭제하라는게 정설인 것 같다.

이는 자식 클래스 객체가 부모클래스의 복사/대입 연산자를 사용하면 Object Slicing이 일어나기 때문이다.

보너스 : static 클래스의 Rule of 5

static 함수와 static 멤버로만 이루어진 클래스는 객체를 생성하지 못하기를 의도하는 경우가 많다.

이 경우는 기본 생성자만 삭제해주고 굳이 다른 것들을 삭제하지 않는 것 같다.

아직 singleton 부모클래스 (ramensoup에서 Application 클래스)는 어떻게 해야하는지 잘 모르겠다.

반응형