[C++] EMCP Item 21: Prefer std::make_unique and std::make_shared to direct use of new
C++14에서 지원 하나, C++11 에서도 다음과 같이 사용 가능
template<typename T, typename... Ts>
std::unique_ptr<T> make_unique(Ts&&... params)
{
return std::unique_ptr<T>(new T(std::forward<Ts>(params)...));
}
make_unique는 단지 new가 생성한 raw pointer로부터 unique_ptr로 생성하는 것이다.
단시 perfect-forward임
이것은 arrays는 지원하지 않고 custom deleter를 지원하지 않음
std namespace에만 넣지 않으면 위와 같이 쉽게 만들어 사용 가능 (충돌 회피)
make_unique
make_shared
make_allocated_shared
make_allocated_shared
: make_shared처럼 동작함 (단, 첫 번째 argument가 allocator object임)
auto upw1(std::make_unique<Widget>()); // with make func
std::unique_ptr<Widget> upw2(new Widget); // without make func
auto spw1(std::make_shared<Widget>()); // with make func
std::shared_ptr<Widget> spw2(new Widget); // without make func
make 함수의 사용 이유
1. 중복 제거
new를 그냥 사용하는 경우는 Widget을 2회 중복 해서 사용함
SW에서 가장 피해야 할 중복의 경우임
compile 시간 증가
bloated object code 가능
오류 야기 가능
make는 그러지 않음
2. exception에 대한 안전
has to do with exception safety.
void procesWidget(std::shared_ptr<Widget> spw, computePriority());
위 경우 Widget의 생성이 먼저 일어날지 priority의 치환이 먼저 일어날지 알 수 없음
(second parameter는 int type 임)
procesWidget(std::shared_ptr<Widget>(new Widget), computePriority());
leak 가능
new Widget 생성
std::shared_ptr<Widget> 생성자가 new로 생성된 pointer에 대한 관리 시작
computePriorty must run
compil가 위 과정을 순서대로 하라는 규약은 없음
new Widget이 shread_ptr 생성자 이전에 수행되어야 하는데,
computePriority가 먼저 실행될 수 있음,
위 순서가 아니라,
new Widget 생성
computePriorty
std::shared_ptr<Widget> 생성자가 new로 생성된 pointer에 대한 관리 시작
위 경우, computePriority는 exception 발생
new Widget은 leak이 됨
shared_ptr에 결코 stored 되지 않음
다른 Basic code block으로 jump 후 return 되면서 dangling 됨
make_shared를 사용하면 이 문제를 피할 수 있음
processWidget(std::makre_shared<Widget>(), computePriority());
이 경우에도 computePriority가 나중에 호출되는데 exception이 발생하면,
shared_ptr의 소멸자를 호출할 수 있음
make_uniqe 역시 마찬가지
std::shared_ptr<Widget> spw(new Widget);
2회 memory allocation 수행
control block까지 할당하기 때문
이 Control block은 shared_ptr 생성자에서 생성됨
auto spw = std::make_shared<Widget>();
make_shared는 하나의 memory chunk를 할당하며
이 chunk가 Widget과 CB 모두 hold
이로서 code의 static size도 줄이고 속도도 빠르게 함
allocate_shared역시 마찬가지 mechanism임
make 함수를 늘 사용할 수는 없음
사용 못하는 경우도 존재함
1. custom deleter 사용 경우
auto widgetDEleter = [](Widget *pw){...};
std::unique_ptr<Widget, decltype(widgetDeleter)> upw(new Widget, widgetDeleter);
std::shared_ptr<Widget> spw(new Widget, widgetDeleter);
위와 같은 식으로 make에서는 사용할 수 없음
2. braced initializer 사용 경우
auto upv = std::make_unique<std::vector<int>>(10, 20);
auto spv = std::make_shared<std::vector<int>>(10, 20);
결과는 vector임
make 함수 내에서 perfect forwarding code는 parentheses를 사용함 (brace - {} -를 사용하지 않음)
위와 달리 brace로 생성하고자 하면, new만 사용 가능함
make도 braced initializer를 요구하게 되나, Item 30에서 설명하듯이 braced initializer는 perfect-forward가 불가능
Item 30에서 workaround를 설명하고 있긴 함
braced initializer로부터 std::initializer_list의 생성에 auto type detection을 사용 (Item 2)
이후 auto created object로 전달
auto initList = { 10, 20 }; // std::initializer_list 를 생성
auto spv = std::make_shared<std::vector<int>>(initList); // 이런식으로 2 steps..
댓글