2023. 11. 20. 00:34ㆍ배움엔 끝이없다/C++
Rule of 3, 5, 0라는 표현을 많이 들어봤지만, 제대로 이해하지 않고 실제로 활용하지도 않고 있었다.
cpp reference에도 관련 항목이 있다.
배경
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() }는
따로 명시하지 않으면 컴파일러가 자동 생성한다.
인자가 있는 생성자를 만들면 기본생성자를 생성하지 않는 규칙과 비슷하게,
사용자가 소멸자를 만들면 이동생성자와 이동대입연산자를 컴파일러가 만들어주지 않는다.
이런 규칙은 뭐를 정의했냐에 따라 다른데, 이걸 외우고 있기 힘들고 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 클래스)는 어떻게 해야하는지 잘 모르겠다.
'배움엔 끝이없다 > C++' 카테고리의 다른 글
floating point precision을 완전히 이해하고 있나? (1) | 2023.11.16 |
---|