원문: http://www2.research.att.com/~bs/C++0xFAQ.html#std-unique_ptr
unique_ptr은 결정적으로 우측 값 참조(rvalue reference)와 이동 시맨틱(move semantic)에 의존한다.
역주
rvalue reference & move semantic: rvalue_references 참조
여기 예외에 안전하지 않은 코드 조각이 있다.
X* f()
{
X* p = new X;
// 특정 작업을 수행 - 예외가 발생할 수 있음
return p;
}
한 가지 해결책은 그 객체를 가리키는 포인터를 unique_ptr 안의 free store에 유지하는 것이다.
역주
free store: 동적 메모리의 두 영역 중, (malloc/free가 아니라) new/delete에 의해 할당되고 해제되는 영역이다. 객체 수명은 그 저장공간이 할당된 것보다 짧을 수 있다. 다시 말해, free store 객체는 즉시 초기화되지 않은 채로 메모리가 할당될 수도 있으며 즉시 메모리가 반납되지 않고 소멸될 수도 있다. 그 시간 동안에 저장공간이 할당되지만 객체의 수명 외부에서 그 저장공간에 접근 가능하며 void*를 통해 조작 가능하다. 하지만 원시-객체의 비 정적 멤버나 멤버 함수들 중의 어떤 것도 접근 불가능하며 주소를 취할 수도 없고 달리 조작될 수도 없다.
http://www.gotw.ca/gotw/009.htm 참조
X* f()
{
unique_ptr<X> p(new X); // {new X}로 써도 되지만 = new X로 쓰면 안 됨
// 특정 작업을 수행 - 예외가 발생할 수 있음
return p.release();
}
지금, 예외가 던져진다면, unique_ptr은 (암시적으로) 가리키고 있는 객체를 소멸시킬 것이다. 그게 기본적인 RAII이다. 그러나 기본(built-in) 포인터를 반환해야 할 필요가 있는 게 아니라면, unique_ptr을 반환함으로써 더 잘 할 수 있을 것이다.
역주 RAII: Resource Acquisition is Initialization, 자원 획득 시에 초기화를 잘 해서 획득하든가, 초기화할 때 혹시 예외가 발생하면 자원을 소멸/해제한다는 의미임
unique_ptr<X> f()
{
unique_ptr<X> p(new X); // {new X}로 써도 되지만 = new X로 쓰면 안 됨
// 특정 작업을 수행 - 예외가 발생할 수 있음
return p; // 소유권이 함수 f()의 외부로 전달됨
}
이 f를 이렇게도 사용할 수 있다.
void g()
{
unique_ptr<X> q = f(); // 이동 생성자를 통해 이동됨
q->memfct(2); // q를 사용함
X x = *q; // q가 가리키는 객체를 복사함
// ...
} // 함수 종료 시 q와 q가 가리키던 객체는 소멸됨
unique_ptr은 이동 시맨틱을 가지기 때문에 f() 호출의 결과인 우측 값(rvalue)을 가지고 q를 초기화하는 것은 단순히 소유권을 q로 옮기는 것이다.
unique_ptr의 사용 방법 중 한 가지는 컨테이너 안의 포인터로 사용하는 것이다. 거기서 예외-안전성 문제를 제외하고는 기본 포인터를 사용할 수 있다. (그리고 컨테이너 원소를 가리키는 포인터의 소멸을 보장하기 위해)
vector<unique_ptr<string>> vs { new string{"Doug"}, new string{"Adams"} };
unique_ptr은 간단한 기본 포인터에 의해 표현되고, 기본 포인터에 비교해서 unique_ptr을 사용하는 부담은 극히 작다. 특히, unique_ptr은 어떤 형태로든 동적 확인을 제공하지 않는다.