weak_ptr은 원시 포인터와 shared_ptr사이에 위치한 스마트 포인터이다. 객체를 안전하게 참조할 수 있지만 참조 횟수를 늘리지 않는다. 정확히는 약참조 횟수를 늘리는데 이는 객체의 수명과 관련이 없다.
weak_ptr은 shared_ptr의 단점인 순환참조나 이미 소멸된 자원을 가리키는 문제를 보완하는데 사용된다.
참조횟수를 증가시키지 않기 때문에 순환참조가 발생하지 않고, 역참조가 불가능하기 때문에 반드시 shared_ptr로 변환하여 사용해야 하는데 자원이 이미 소멸된 경우에는 비어있는 shared_ptr을 반환해주므로 문제가 발생하지 않는다.
std::shared_ptr<Widget> sp(new Widget);
std::weak_ptr<Widget> wp(sp);
std::weak_ptr<Widget> wp2(wp);
shared_ptr의 문제점을 보완하기 위한 스마트 포인터이기 때문에 원시 포인터를 통해 직접적인 생성은 불가능하고 shared_ptr이나 weak_ptr을 통해서만 생성이 가능하다.
weak_ptr로부터 shared_ptr을 생성하는 방법은 두 가지가 있다. 이 때, weak_ptr의 만료여부에 따라 동작이 다르다.
std::shared_ptr<Widget> spw(new Widget);
std::weak_ptr<Widget> wpw(spw);
/* 이후 swp 소멸 */
/* Case 1 */
std::shared_ptr<Widget> spw1 = wpw.lock(); // 가리키는 원시 포인터는 nullptr
/* Case 2 */
std::shared_ptr<Widget> spw2(wpw); // std::bad_weak_ptr 예외 발생
weap_ptr의 사용이 유용한 예를 몇가지 들어보도록 하자.
첫째로 어떤 함수의 호출 비용이 꽤 크고 그 반환값이 반복되어 사용되는 경우가 많다고 하면 그 반환값들을 캐싱하여 사용하는 것이 효율적일 것이다. 하지만 계속 캐시에 담아 둔다면 그 자체로 성능문제가 또 발생하기 때문에 더 이상 쓰이지 않는 값들은 캐시에서 삭제하는 것이 또 다른 최적화 방안이 될 것이다.
이 때, 팩토리 함수의 반환 타입을 unique_ptr(이전에는 좋다고 했지만)로 두는 것은 그다지 좋지 못하다. 왜냐면 캐시 값들이 수시로 삭제되기 때문에 weak_ptr를 사용하는것이 좋고 weak_ptr를 사용하려면 반드시 반환 타입이 shared_ptr이어야 한다.
두 번째 사례로 관찰자 패턴에서 유용하게 사용된다.
관찰 대상은 관찰자의 수명에는 관심이 없고 파괴된 관찰자에 접근하는지가 중요하다. 이 때 weak_ptr를 사용하면 만료 여부를 먼저 확인하고 관찰자에게 메시지를 날릴 수 있다.
weak_ptr는 shared_ptr과 크기가 같고 shared_ptr가 사용하는 제어 블록과 같은 제어 블록에 접근하여 사용한다.
◾ std::shared_ptr처럼 작동하되 대상을 잃을 수도 있는 포인터가 필요하면 std::weak_ptr를 사용하라.
◾ std::weak_ptr의 잠재적인 용도로는 캐싱, 관찰자 목록, 그리고 std::shared_ptr 순환 참조 방지가 있다.
'도서 > Effective Modern C++' 카테고리의 다른 글
[4장] 항목 22 : Pimpl 관용구를 사용할 때에는 특수 멤버 함수들을 구현 파일에서 정의하라 (0) | 2023.01.24 |
---|---|
[4장] 항목 21 : new를 직접 사용하는 것보다 std::make_unique와 std::make_shared를 선호하라 (0) | 2023.01.20 |
[4장] 항목 18 : 소유권 독점 자원의 관리에는 std::unique_ptr를 사용하라 (0) | 2023.01.19 |
[3장] 항목 17 : 특수 멤버 함수들의 자동 작성 조건을 숙지하라 (0) | 2023.01.17 |
[3장] 항목 16 : const 멤버 함수를 스레드에 안전하게 작성하라 (0) | 2023.01.17 |