[C++] 스마트 포인터: unique_ptr

이전 포스트: "자원 관리와 스마트 포인터"
https://m.blog.naver.com/sssang97/221805519374

unique_ptr은 이름 그대로 유일성(unique)를 갖는 포인터다. 이 포인터의 포인터값은 복사될 수 없고, 하나의 unique_ptr 객체만이 하나의 포인터값을 쥐고 있을 수 있다.
이런걸 '소유권'이라고 부르곤 한다.

근데 이렇게 소유권을 부여하는 것으로 얻을 수 있는 게 뭘까?
하나의 unique_ptr이 과감하게 할당을 해제해도, 그로 인해 dangling 포인터(유효하지 않은 포인터)가 발생하지 않을 거라는 보장이 생기는 것이다!
한놈만 쥐고 있는거니까.

그리고 unique_ptr은 객체가 영역(scope)을 벗어나 자신의 소멸자가 호출될 때 자동으로 할당을 해제한다.
근데 영역을 넘나들게 하고 싶다면 어떨까? 그냥 영역에 갇힌 채로만 사용해야 하는 건가?
당연히 아니다. 그냥 C++11의 move semantic을 사용해 유일성을 보존한 채로 객체를 이동시키면 된다.

백문불여일견이라, 코드부터 한번 봐보자.
일단 생성과 소멸을 확인하기 위해, 전용 래퍼 타입을 구현해봤다. 별건없다. image

image

그럼 이제 저걸로 스마트포인터를 써보자. unique_ptr은 템플릿 클래스다. 포인터의 원본타입을 템플릿 인자로 받는다.
그리고 생성자론 동적할당된 포인터값을 받는다.
게다가 *, -> 등의 연산자가 정의되어있어 그냥 포인터와 마찬가지로 사용할 수 있다. image

image 잘 돌아가는걸 볼 수 있다.
그리고 동적할당된 Int의 소멸자가 자동으로 호출됐다. main 함수가 종료되며 unique_ptr의 소멸자가 작동, 객체를 정리한 것이다.


복사 금지
그리고 앞서 언급했듯이, unique_ptr은 유일성을 보장한다. 따라서 복사는 불가능하다. image

image 복사생성자와 복사할당연산자가 전부 삭제된 함수라 그렇다.
unique_ptr의 값을 동적으로 여기저기 넘기고 싶다면, 그냥 참조로 넘기거나 이동시켜야 한다. image

image 자세한 것은 무브시맨틱 포스트들을 참조하길 바란다.
https://m.blog.naver.com/sssang97/221472346738


복사의 예외?
unique_ptr의 복사는 원천적으로 금지되어있지만, 한가지 예외가 존재한다.
바로 함수의 반환값 최적화(Return Value Optimization)로 날라온 값은 받을 수 있다는 것이다.
반환값 최적화가 수행되는 함수의 반환값은, 사실상 이동으로 넘어오기 때문이다. image

image 그래서 이럴때는 굳이 반환타입을 &&로 해놓고 이동을 사용할 필요가 없다.


포인터의 생존 확인
unique_ptr는 이동시키면 원본 포인터 객체가 죽는다. 하지만 내부 객체가 날라가는 거지, 사용이 불가능해지는 건 아니라, 유효성을 체크해야 할 수도 있다.
포인터 객체의 생사 확인은 bool 캐스팅으로 해결할 수 있다.
이런식으로. image

image


성능?
unique_ptr의 성능은 일반 포인터와 거의 같다고 보면 된다.
그냥 소멸자에서 delete해주는게 다라서,
추가로 메모리를 먹는 것도 없고... 그냥 가볍게 사용해도 무난하다.


좀 더 편리한 생성: make_unique
위에선 스마트포인터를 생성할때, 생성자 인자로 직접 new를 해서 보내줬었다.
근데 꼭 그럴 필요가 있는걸까?
게다가 저러면 타입 표현이 중복된다.
unique_ptr<Int> p(new Int(99));
제너릭 인자로 Int 하나, 또 new할때 Int 하나.
보기에 썩 좋지는 않다.

make_unique는 이러한 표현을 좀더 간소화해주는 생성용 함수다.
제너릭 인자로 타입을 써주고, 함수 인자에는 생성자의 인자들만 가변인자로 보내주면 된다.
그러면 알아서 내부적으로 new 생성해준다. image

image 아 근데 이건 C++14부터 제공된다.

이외에도 여러가지 기능이 있으니 자세한 사항은 문서를 참조하길 바란다.

unique_ptr 규격 참조
https://en.cppreference.com/w/cpp/memory/unique_ptr