programming/programming general

[C++] EMCP Item 20: use std::weak_ptr for std::shared_ptr-like pointers that can dangle.

Roien 2018. 3. 25.
반응형

   shared_ptr 처럼 작동하지만 대상을 잃을 수도 있는 경우 shared_ptr 사용


   대상의 존재 유/무를 검출 가능해야 함

   weak_ptr은 shared_ptr의 보강 용도임



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

   std::weak_ptr<Widget> wpw(spw);

   spw = nullptr;

   if (wpw.expired()) …   <-- expired 되었음을 확인 할 수 있음



   weak_ptr을 사용하는 경우는,

       caching

       observer lists

       shared_ptr cycle의 prevention


   shared_ptr 끼리의 상호 참조는,

       서로 reference count를 지니기에,

       서로 소멸을 시킬 수 없게 만들기도 함 -> leak


   이런 경우, weak_ptr을 사용해서

       shared_ptr의 소멸 시, weak_ptr이 dangle 되게 함

       weak_ptr은 dangle 상황을 감지할 수 있음


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

       std::weak_ptr<Widget> wpw(spw);   


       if (wpw.expired()) {

           ...



weak_ptr에서 shared_ptr 획득 방법

  1) lock 사용

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

    auto spw2 = wpw.lock();


  2) 생성자 인수로 지정

    std::shared_ptr<Widget> spw3(wpw);


       argument로 받을 시, wpw가 expired 된 상태면,

       shared_ptr 내 std::bad_weak_ptr exception 이 발생함



왜? weak_ptr을 사용하나?

  1) cacheable 객체

  모든 ref. 소멸 시 함께 소멸 시킬 필요가 있는 경우


    std::shared_ptr<const Widget> fastLoadWidget(WidgetID id) {

        static std::unordered_map<WidgetID,

        std::weak_ptr<const Widget>> cache;

        auto objPtr = cache[id].lock(); // objPtr is std::shared_ptr

                                        // to cached object (or null

                                        // if object's not in cache)

        if (!objPtr) {                  // if not in cache,

            objPtr = loadWidget(id);    // load it

            cache[id] = objPtr;         // cache it

        }

        return objPtr;

    }


    unordered_map 즉, hash table contaner 사용



    그냥, 

    <ID, shared_ptr<...>>로 table을 구성하면 되지 않나?

    이게 더 쉬운것 아닌가?


  2) observer design pattern

    관찰 대상은 자신이 파괴된 관찰자에 접근하는 일이 없도록 보장할 필요가 있음


  3) circular dependency


    +---+  shared_ptr      +---+    shared_ptr   +---+

    | A |----------------> | B | <-------------- | C |

    +---+                  +---+                 +---+



    +---+  shared_ptr      +---+    shared_ptr   +---+

    | A |----------------> | B | <-------------- | C |

    +---+                  +---+                 +---+

      ^                      |

      |     shared_ptr       |

      +----------------------+


        위 경우 순환 고리(cycle)를 형성하기에

        A와 B 둘 다 파괴되지 못함



    +---+  shared_ptr      +---+    shared_ptr   +---+

    | A |----------------> | B | <-------------- | C |

    +---+                  +---+                 +---+

      ^                      |

      |    weak_ptr          |

      +----------------------+


        weak_ptr 사용 시 

        cycle이 있지만, 파괴될 수 있음


    tree등 엄격히 계층적인 자료구조에서는 보통 부모만 자식을 소유

    부모 노드 파괴 시 자식 노드도 파괴됨

    따라서 부모에서 자식으로의 링크는 일반적으로 unique_ptr을 사용하는 것이 최선


    엄격히 통제적이지 않는 자료 구조에서는 weak_ptr 사용 가능



효율성

  weak_ptr은 shared_ptr과 본질적으로 동일

  그 크기가 동일

  단, weak_ptr은 객체의 소유권 공유에 참여하지 않음



Things to remember

  1) shared_ptr과 동일하나 대상을 잃어도 되는 pointer로서 weak_ptr 사용

  2) 사용 경우는 caching, observer pattern, cycle 제거 등이 있음

반응형

댓글