티스토리 뷰

 앞서 살펴본 스마트 포인터들 중에서 가장 이해하기 어려운 것이 weak_ptr 이다. unique_ptr 와 shared_ptr 는 목적이 분명하지만 weak_ptr 는 경험하지 않는다면 언제 써야하는지 알기 어렵다. 이 포스트에서 weak_ptr 를 사용해야하는 경우를 살펴보자.

사용법

 weak_ptr 는 언제든 소멸될 수 있는 객체를 사용할 때 쓰는 스마트 포인터이다. weak_ptr 는 shared_ptr 와 함께 사용하는 스마트 포인터이지만 reference_count 를 증가시키지 않는다. 다시 말하면 객체의 소멸에 관여하지 않는다는 뜻이다. 그로 인한 문제로 사용하려고 하는 순간 이미 객체가 소멸될 수도 있는 것이다. 만약 객체를 사용할 때, 객체가 소멸되는 경우 crash 가 발생하게 되는 것이다. 이러한 문제 상황이 발생하는 것을 막기 위하여 weak_ptr 는 shared_ptr 로 변환해서 사용해야만 한다. 

std::shared_ptr<Widget> spw1 = wpw.lock();

auto spw2 = wpw.lock();

 

사용하는 상황

캐시를 사용하는 경우

 데이터 베이스에서 가져온 데이터를 캐시를 해놓는다고 가정하자. 캐시를 해놓은 객체를 소멸시키지 않는다면 성능상의 문제가 발생할 것이므로 사용하지 않는 객체는 소멸을 시켜야한다. 만약 캐시에서 reference count 를 증가시킨다면 소멸시켜야하는 시점에 대해서 알기 어려울 것이다. reference count 를 증가시키지 않으면서 언제든 소멸시킬 수 있는 상황 속에서 weak_ptr 는 아주 적절하다. 다음과 같이 사용할 수 있다.

std::shared_ptr<const Widget> fastLoadWidget(WidgetID id)
{
    static std::unordered_map<WidgetID,
                              std::weak_ptr<const Widget>> cache;
                              
    auto objPtr = cache[id].lock();
    
    if (!objPtr) {
        objPtr = loadWidget(id);
        cache[id] = objPtr;
    }
    return objPtr;
}

 

옵저버 패턴

 옵저버 패턴에서 관찰자는 관찰 대상들을 담는 컨테이너를 사용한다. 관찰자들은 관찰 대상들의 소멸 여부에는 관심이 있지만 수명에는 관여하지 않는다. 따라서 옵저버 패턴을 사용하는 상황이라면 weak_ptr 를 사용하는 것이 적절하다.

 

순환참조

 스마트 포인터를 사용하는 경우에 발생하는 가장 큰 문제이며 weak_ptr 를 사용하는 가장 큰 이유이기도 하다. A와 B, C 객체가 존재한다고 하자. A 와 C 가 B 객체를 소유하는 상황을 가정해보자. 이 상황 속에서 B 가 A 를 가르키는 포인터가 필요하게 된 경우 어떤 포인터를 사용해야 하는지 알아보자.

  • 생 포인터

 위의 상황에서 A 가 소멸된다면 B 는 소멸되었는지 알 수가 없어 널 포인터에 접근하게 된다.

  • std::shared_ptr

 A 가 B 를 소유하고 B 가 A 를 소유하게 된다. 순환참조를 하게 되는 상황으로 서로 영원히 소멸되지 않는다.

  • std::weak_ptr

 가장 이상적인 선택으로 널 포인터에 접근하지도 순환참조가 발생하지도 않는다.

 

 

참고 서적

스콧 마이어스, Effective Modern C++
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함