티스토리 뷰
C++을 사용하면서 자료를 다루는 class는 수도 없이 많이 사용될 것이다.
하지만 그 자료가 완전히 메모리에 안전한 인스턴스일 수도 있고, raw pointer일 수도 있을 것이다.
이 때에 맞추어 각 class를 구성하는 방법에 대하여 알아보자.
Rule of zero :
소멸자, 복사/이동 생성자, 복사/이동 할당연산자가 정의되어있는 class의 wrapper class는 다음의 꼴을 가진다.
class Test { private: std::string s; public: Test(const std::string &s) : s(s) {} };
소멸자, 복사, 이동 생성자, 복사, 이동 할당연산자가 정의되어있는 class는 OOP 단일 책임 원칙에 따라 새로운 소멸자, 생성자, 할당연산자가 정의되어선 안된다.
Rule of three :
Raw pointer를 가지고 있는 wrapper class는 적당한 할당과 해제가 정의되어있는 소멸자, 복사 생성자, 복사 할당연산자를 정의해야 한다.
class Test { private: char* s; public: Test() : s(new char[0]) {} Test(const char* s, std::size_t size) : s(new char[size + 1]) { std::copy(s, s + size + 1, this->s); } Test(const Test& test) : Test(test.s, std::strlen(test.s)) {} ~Test() { delete[] s; } Test& operator=(const Test& test) { delete[] s; auto size = std::strlen(test.s); s = new char[size + 1]; std::copy(test.s, test.s + size, s); return *this; } };
Rule of zero에서와 같이 raw pointer를 제어하는 기능을 포함하지 않는다면(default를 사용한다면) Test class는 복사할 때 raw pointer에 대한 얕은 복사만 이루어지게 된다.
하지만 이런 방법으로는 C++11에서 도입된 이동 연산을 가리게 된다.
Rule of five :
굳이 모든 자료가 깊은 복사가 이루어질 필요없이 일시적으로 생성되는 객체의 자료를 이동하는 방법을 사용할 수도 있어야할 것이다.
다음의 경우에서,
auto s = "Hello World!"; Test test; test = Test(s, std::strlen(s));
Rule of three만 적용되어있는 Test class는 test를 생성하는 과정에서 불필요한 복사를 야기시킨다.
이에 따라 rule of three에서 가리게 되었던 이동 생성자, 할당 연산자를 다시 정의할 필요가 있다.
class Test { private: char* s; public: Test() : s(new char[0]) {} Test(const char* s, std::size_t size) : s(new char[size + 1]) { std::copy(s, s + size + 1, this->s); } Test(const Test& test) : Test(test.s, std::strlen(test.s)) {} Test(Test&& test) noexcept : s(std::exchange(test.s, nullptr)) {} ~Test() { delete[] s; } Test& operator=(const Test& test) { delete[] s; auto size = std::strlen(test.s); s = new char[size + 1]; std::copy(test.s, test.s + size, s); return *this; } Test& operator=(Test&& test) { std::swap(s, test.s); return *this; } };
이때 다시 위의 예제를 실행한다면, 이동 할당연산자가 호출되어 바로 사라질 객체인 Test(s, std::strlen(s))으로부터의 복사를 방지할 수 있다.
정리하자면, raw pointer를 제어하는 class의 경우 각각에 맞는 할당과 해제를 맡을 복사 생성자, 소멸자, 할당연산자를 정의해야하고
C++11 이상에서 연산의 손실을 방지하기 위해서는 이동 생성자와 할당연산자 또한 직접 정의해야한다.
'C++' 카테고리의 다른 글
[C++] 안전한 포인터 설계 (0) | 2019.01.26 |
---|
- Total
- Today
- Yesterday
- inline class
- Kotlin
- vector
- PipelineContext
- C
- linaro
- 포인터
- d802
- c++ struct
- g2 korea
- C++ 업캐스팅
- LG
- cyanogenmod
- dokdo 4.0.3
- G2
- C++
- rule_of_five
- Java
- CM10.2
- dokdo project
- c++ 상속
- f320k
- CM11
- dokdo-project
- OOP
- c++11
- rule_of_three
- 객체지향
- f320s
- nodeal
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |