카테고리 없음
[C++] EMCP Item 18: 독점적 소유권 (Exclusive-ownership) 자원 관리 시, unique_ptr을 사용 하라
반응형
기본적으로 unique_ptr은 raw pointer와 동일 size (라고 가정하는 것이 합리적)
. 성능과 memory 자원에 영향을 받지 않음
unique_ptr
copy 불가 (move-only 임)
- 동일 소유 상황을 막기 위함
- 이동만 허용
소유하는 자원의 destruction은 delete로 수행됨
ex.
factory with unique_ptr
class Investment { … };
class Stock: public Investment { … };
class Bond: public Investment { … };
class RealEstate: public Investment { … };
// a factory function
template<typename... Ts> // return std::unique_ptr
std::unique_ptr<Investment> // to an object created
makeInvestment(Ts&&... params); // from the given args
auto pInvestment = makeInvestment( arguments );
unique_ptr이 container로 이동 되면, container elemetn는
객체의 data member로 이동됨
object가 나중에 제거되면 object의 unique_ptr data member도 제거됨
이 ownership chain이 exception이나 다른 atypical control flow(early function return, loop의 break 등)에 의해서 interrupt되면, unique_ptr의 소멸자가 불리게 되며, resource도 제거됨
ex. dustom deleter 사용하는 unique_ptr 사용 factory function
자원 생성/소멸에 대해서 신경 쓸 필요가 없어짐
auto delInvmt = [](Investment* pInvestment) // custom
{ // deleter
makeLogEntry(pInvestment); // (a lambda
delete pInvestment; // expression)
};
template<typename... Ts> // revised
std::unique_ptr<Investment, decltype(delInvmt)> // return type
makeInvestment(Ts&&... params)
{
std::unique_ptr<Investment, decltype(delInvmt)> // ptr to be returned
pInv(nullptr, delInvmt);
if ( /* a Stock object should be created */ )
{
pInv.reset(new Stock(std::forward<Ts>(params)...)); // perfect forwarding
}
else if ( /* a Bond object should be created */ )
{
pInv.reset(new Bond(std::forward<Ts>(params)...));
}
else if ( /* a RealEstate object should be created */ )
{
pInv.reset(new RealEstate(std::forward<Ts>(params)...));
}
return pInv;
}
unique_ptr을 사용
: 소멸이 정확히 한 번만 일어남을 보장됨을 의미
smart pointer로의 assign은 불가하여 reset을 사용
custom deleter는 Investment *를 받음
이를 위해 Investment는 반드시 virtual destructor를 지녀야 함
class Investment {
public:
… // essential
virtual ~Investment(); // design
… // component!
};
C++14에서는 함수 반환 형식의 deduction(연혁) 지원
makeInvestment 함수 내에서 소멸자를 허용
template<typename... Ts>
auto makeInvestment(Ts&&... params) // C++14
{
auto delInvmt = [](Investment* pInvestment) // this is now
{ // inside
makeLogEntry(pInvestment); // makedelete
pInvestment; // Investment
};
std::unique_ptr<Investment, decltype(delInvmt)>
pInv(nullptr, delInvmt);
...
}
기본 삭제자(즉 delete)를 사용할 때에는 unique_ptr 객체의 크기가 raw pointer 크기와 같으리라고 가정하는 것이 합당
그러나 custom deleter를 사용하면 상황이 달라짐 - 이 경우, unique_ptr의 크기는 1 or 2 words 증가
ex. custom deleter
auto delInvmt1 = [](Investment* pInvestment) // <-- custom deleter
{
makeLogEntry(pInvestment); // as stateless lambda
delete pInvestment;
};
template<typename... Ts> // return type has size of
std::unique_ptr<Investment, decltype(delInvmt1)> // Investment*
makeInvestment(Ts&&... args);
void delInvmt2(Investment* pInvestment) // custom deleter
{
makeLogEntry(pInvestment); // as function
delete pInvestment;
}
template<typename... Ts> // return type has size of Investment*
std::unique_ptr<Investment, // plus at least size of function pointer!
void (*)(Investment*)>
makeInvestment(Ts&&... params);
unique_ptr의 흔한 용도가 factory function 만은 아님
pimpl로 더 인기 있음
unique_ptr에서 shared_ptr로의 변환이 가능함
std::shared_ptr<Investment> sp = makeInvestment(param..);
stateless function objects
: Lambda expression with no captures
stateless function object로는 function pointer 보다,
Lambda with no captures가 더 선호됨
unique_ptr은
1) unique_ptr<T>
2_ unique_ptr<T[]>
array는 사용하지 않는 것이 좋음
std::array, vector, string 등이 있기 때문
unique_ptr<T[]>를 사용할 만한 경우는,
C-like API를 사용하는 경우에만 한정됨
3) shared_ptr로의 conversion
std::shared_ptr<Investment> sp =
makeInvestment( arguments );
Things to remember
1) unique_ptr은 독점 소유권 semantics를 지닌 자원 관리를 위한 작고 빠른 이동 전용 smart pointer임
2) 기본적으로 파괴는 delete를 통해 일어나나, custom deleter 지정 가능
함수 pointer를 사용하면 unique_ptr 객체의 크기가 커짐
3) unique_ptr을 shared_ptr로 쉽게 변환 가능
반응형
댓글