카테고리 없음

[C++] EMCP Item 21: Prefer std::make_unique and std::make_shared to direct use of new

Roien 2018. 3. 25.
반응형

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..

반응형

댓글