KoreanFoodie's Study

Effective Modern C++ | 항목 20 : std::shared_ptr 처럼 작동하되 대상을 잃을 수도 있는 포인터가 필요하면 std::weak_ptr 를 사용하라 본문

Tutorials/C++ : Advanced

Effective Modern C++ | 항목 20 : std::shared_ptr 처럼 작동하되 대상을 잃을 수도 있는 포인터가 필요하면 std::weak_ptr 를 사용하라

GoldGiver 2022. 10. 26. 09:56

C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!

항목 20 : std::shared_ptr 처럼 작동하되 대상을 잃을 수도 있는 포인터가 필요하면 std::weak_ptr 를 사용하라

핵심 :

1. std::shared_ptr 처럼 작동하되 대상을 잃을 수도 있는 포인터가 필요하면 std::weak_ptr 를 사용하라
2. std::weak_ptr 의 잠재적인 용도로는 캐싱, 관찰자 목록, 그리고 std::shared_ptr 순환 고리 방지가 있다.


std::weak_ptr 는 std::shared_ptr 처럼 동작하되, 객체의 참조 횟수에는 영향을 끼치지 않는다. 대상을 잃은 std::weak_ptr 를 가리켜 만료되었다(expired) 라고 표현하는데, 다음과 같이 판정할 수 있다.

class Widget {};

auto spw = std::make_shared<Widget>();

// Widget 의 참조 횟수는 여전히 1
std::weak_ptr<Widget> wpw(spw);

// Widget 파괴, wpw 는 대상을 잃음
spw = nullptr;

// wpw 가 객체를 가리키지 않으면...
if (wpw.expired()) ...

그런데 weak_ptr 에는 역참조 연산이 없으며, 만료 점검과 역참조 연산을 분리하면 경쟁 조건이 발생할 수 있다. 즉, 다른 스레드가 만료 점검 이후 해당 객체를 파괴할 수 있기 때문이다. 따라서, weak_ptr 가 가리키는 객체에 대한 접근은 원자적으로 일어나야 한다.
첫 번째 방법은 std::weak_ptr::lock 을 이용해서 std::shared_ptr 객체를 받는 것이다.

// wpw 가 만료이면 spw1 은 널
std::shared_ptr<Widget> spw1 = wpw.lock();

// 위와 동일하나 auto 사용
auto spw2 = wpw.lock();

또 다른 방법은 std::weak_ptr 를 인수로 받는 std::shared_ptr 생성자를 사용하는 것이다. 만일 std::weak_ptr 가 만료되었다면 예외가 발생한다.

// wpw 가 만료이면 std::bad_weak_ptr 가 발생
std::shared_ptr<Widget> spw3(wpw);


std::weak_ptr 가 유용하게 쓰이는 경우에 대해 알아보자. 첫 번째로는 캐시를 예로 들 수 있다. 어떤 객체의 정보를 조회하는데 많은 비용이 든다고 가정했을때, 이를 캐시화하여 빠르게 로딩하는 함수를 추가로 만들어 볼 수 있을 것이다. 그런데 해당 객체가 로드될때 std::unique_ptr 형식으로 관리된다고 하면, 해당 객체를 참조하는 캐시는 std::shared_ptr 가 아닌 std::weak_ptr 이어야 할 것이다. 캐시는 해당 객체를 '소유' 하고 있는 개념이 아니기 때문이다!
두 번째로는, 관찰자(Observer) 설계 패턴이 있다. 관찰 대상 입장에서 관찰자가 파괴되었는지 아닌지를 검출하는 데 있어서, 관찰자들을 std::weak_ptr 들의 컨테이너로 관리하는 방식이다.
마지막으로, 상호 참조(혹은 순환 참조)가 일어날 수 있는 경우이다. 특히 트리를 구현할 때, std::weak_ptr 를 사용하여 구현할 수도 있다.

효율성 측면에서는 std::shared_ptr 와 std::weak_ptr 는 동일하다. 다만 std::weak_ptr 는 참조 횟수에 영향을 미치지 않으며, 피지칭 객체를 소유하는 개념이 아니라는 사실만 기억하자.

Comments