Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership resource management...

22
Effective Modern C++ Study C++ Korea Speaker : Yun Seok-joon ( [email protected] )

Transcript of Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership resource management...

Effective Modern C++ StudyC++ Korea

Speaker : Yun Seok-joon ( [email protected] )

1. C++11 Smart Pointer

2. std::unique_ptr

3. Custom deleter

4. Etc…

1. C++11 Smart Pointer

Effective Modern C++ StudyC++ Korea5

1. Single Object 인지Array인지알수없다.

2. Pointer 사용후 delete해야하는지알수없다.다른곳에서사용중인데 delete하면 -> run time error

사용하는곳이없는데 delete안하면 -> memory leak

3. delete로지워야하는지, delete []로지워야하는지알수없다.

4. delete, delete [] 둘중뭐로해제해도 error 없이해제해준다.하지만잘못했을땐나중에 run time error 가…

5. 이미해제되었는지(dangling pointer) 여부를알수없다.정확한해제방법을알아도 dangling pointer를해제하면 -> run time error

Effective Modern C++ StudyC++ Korea6

1. std::auto_ptr

- C++98부터있었으나별로사용되지않았음

(C++98에는Move Sementics가없어서 MOVE를못하고COPY로수행되었음)

- C++11에서도잘사용되지는않음

2. std::unique_ptr- 개체를공유하지않고, 유일할소유권으로관리하기위한 Smart Pointer

- 모든면에서 std::auto_ptr보다좋음

Effective Modern C++ StudyC++ Korea7

3. std::shared_ptr

- 개체를공유하면서관리하기위한 Smart Pointer

- 해당개체를아무도사용하지않을때자동으로해제해줌

4. std::weak_ptr- std::shared_ptr의순환참조를방지

- dangling pointer 여부확인이가능

Effective Modern C++ StudyC++ Korea9

1. Raw Pointer 만큼작고빠르며, 거의비슷한방법으로사용이가능

custom deleter를사용안하면크기도같음

2. Default로는개체해제시 delete를사용하지만, custom deleter도가능

3. 개체에대해서독점소유권을가지며, 포인터해제시개체도해제됨

4. MOVE only 타입

Effective Modern C++ StudyC++ Korea10

user

① 개체를 내놓아라~

Factory② 옜다. 개체.

(사실은 개체가 아니라 Pointer만 준건 비밀 ?)

③ 사용 후 해제의 의무

(다 알거든. 개체가 아니라 Pointer만 준거. 근데 해제는 나보고 ?)

std::unique_ptr의동작과완벽하게일치 + 자동해제는 뽀나스~

Effective Modern C++ StudyC++ Korea11

elf

Temple Knight Elemental Summoner Elder

Effective Modern C++ StudyC++ Korea12

class ElvenUnit { ... };class TempleKnight : public ElvenUnit { ... };class ElemetalSummoner : public ElvenUnit { ... };class Elder : public ElvenUnit { ... };

template<typename... Ts>std::unique_ptr<ElvenUnit> MakeElvenUnit(Ts&&... params){

std::unique_ptr<ElvenUnit> pElf(nullptr); // make null unique_ptrif ( /*is TK*/) { pElf.reset(new TempleKnight(std::forward<Ts>(params)...)); }else if ( /*is ES*/) { pElf.reset(new ElemetalSummoner(std::forward<Ts>(params)...)); }else if ( /*is Ed*/) { pElf.reset(new Elder(std::forward<Ts>(params)...)); }

return pElf;}

{

auto pElf = MakeElvenUnit( arguments ); // std::unique_ptr<ElvenUnit>...

} // destroy *pElf

Effective Modern C++ StudyC++ Korea13

std::vector<std::unique_ptr<ElvenUnit>> ElvenArmy;

{auto pElf = MakeElvenUnit( arguments ); // std::unique_ptr<ElvenUnit>ElvenArmy.emplace_back(std::move(pElf)); // set pElf to null

}

Effective Modern C++ StudyC++ Korea15

1. 함수 Pointer 또는 Lambda 로개체삭제방법설정이가능

default로는 delete가사용

2. 원래 std::unique_ptr의크기는 Raw Pointer와같이 1 word 지만,

deleter에대한 Pointer가추가로필요해져서 2 word가됨

stateless function object (e.g. captureless lambda )의경우는크기패널티가없음

3. std::unique_ptr타입의일부

custom deleter의 state 만큼 std::unique_ptr의크기가커짐

고로 deleter는 function 보다는 captureless lambd가더바람직

Effective Modern C++ StudyC++ Korea16

auto DieElf = [](ElvenUnit* pElf) { // custom deleter (using lambda expression)AddGuildExp(pElf);delete pElf;

};

template<typename... Ts>std::unique_ptr<ElvenUnit, decltype(DieElf)> MakeElvenUnit(Ts&&... params){

std::unique_ptr<ElvenUnit, decltype(DieElf)> pElf(nullptr, DieElf); // make null unique_ptr

if ( /* Temple Knight 를 만들 조건이면 */) {pElf.reset(new TempleKnight(std::forward<Ts>(params)...));

}else if ( /* Elemental Summoner 를 만들 조건이면 */) {

pElf.reset(new ElemetalSummoner(std::forward<Ts>(params)...));}else if ( /* Elder를 만들 조건이면 */) {

pElf.reset(new Elder(std::forward<Ts>(params)...));}return pElf;

}

Effective Modern C++ StudyC++ Korea17

template<typename... Ts>auto MakeElvenUnit(Ts&&... params){

auto DieElf = [](ElvenUnit* pElf) { // custom deleter (using lambda expression)AddGuildExp(pElf);delete pElf;

};

std::unique_ptr<ElvenUnit, decltype(DieElf)> pElf(nullptr, DieElf); // make null unique_ptr

if ( /* Temple Knight 를 만들 조건이면 */) {pElf.reset(new TempleKnight(std::forward<Ts>(params)...));

}else if ( /* Elemental Summoner 를 만들 조건이면 */) {

pElf.reset(new ElemetalSummoner(std::forward<Ts>(params)...));}else if ( /* Elder 를 만들 조건이면 */) {

pElf.reset(new Elder(std::forward<Ts>(params)...));}return pElf;

}

Effective Modern C++ StudyC++ Korea18

auto DieElf = [](ElvenUnit* pElf) // stateless lambda{

AddGuildExp(pElf);delete pElf;

};

template<typename... Ts>std::unique_ptr<ElvenUnit, decltype(DieElf)> // return type has size of EvenUnit*MakeElvenUnit(Ts&&... params);

void DieElf(ElvenUnit* pElf) // function{

AddGuildExp(pElf);delete pElf;

};

template<typename... Ts>std::unique_ptr<ElvenUnit, void (*)(ElvenUnit*)> // return type has size of EvenUnit*MakeElvenUnit(Ts&&... params); // + at least size of function pointer !

Effective Modern C++ StudyC++ Korea20

1. Single Object (std::unique_ptr<T>), 배열 (std::unique_ptr<T[]>)

둘다사용이가능

std::unique_ptr API 는각각다르게처리

Single Object 에대해서는 Index 연산자 [ ] 가없고,

배열에대해서는 dereference 연산자 ( * , -> )가없음

2. Std::unique_ptr은 std::shared_ptr로쉽게캐스팅

반대는불가능

그래서 Factory method에서일단 std::unique_ptr로받는게더적절

Effective Modern C++ StudyC++ Korea22

• std::unique_ptr은 작고, 빠르고, MOVE-only 스마트 포인터이며,

자원을 독점적으로 관리해 줍니다.

• Default로는 개체 해제에 delete를 사용하지만,

custom deleter를 사용 할 수도 있습니다.

Stateless lambda를 사용하면 크기 패널티가 없지만,

그렇지 않은 경우는 pointer 1개 크기 + state 크기만큼 더 커집니다.

• std::unique_ptr에서 std::shared_ptr로쉽게 캐스팅이 가능합니다.

그 반대는 절대로 안됩니다.

http://devluna.blogspot.kr/2015/04/item-18-stdunqiueptr.html

[email protected]