Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Effective Modern C++ Item19, Item20

Effective Modern C++ Item19, Item20

shared_ptr と weak_ptr の概要および内部的な話など.
Effective Modern C++ 読書会 4/22(水)

Dea1add99f4cf942792c0f185aa2f2fd?s=128

Linda_pp

April 22, 2015
Tweet

Transcript

  1. Effective Modern C++ ಡॻձ Item 19, Item 20

  2. @Linda_pp @rhysd  झຯͰίϯύΠϥͱ͔ͭͬͯ͘·͢ɽ https://github.com/rhysd/Dachs

  3. Item 19: ॴ༗ݖΛڞ༗͍ͨ͠Ϧ ιʔεͷ؅ཧʹ͸ std::shared_ptr Λ࢖͓͏

  4. C++ ʹ͓͚ΔϝϞϦ؅ཧ Q.ʮͳΜͯݪ࢝తͳΜͩʂ1960೥୅ͷLisp ʹԿ΋ֶ͹ͳ ͔ͬͨͷ͔ʁϦιʔε͸ਓؒͰͳ͘Ϛγϯ͕؅ཧ͢΂͖ͩʯ ! A.ʮϝϞϦ͚͕ͩϦιʔεͰ͸ͳ͍͠ɼϝϞϦ͕։์͞Ε ΔλΠϛϯά͕෼͔Βͳ͍ͷ͸ࠔΔɽԶୡ͸ී௨ͷ༧ଌͰ ͖ΔσετϥΫλ͕ྑ͍Μͩɽʯ Q

  5. C++ ʹ͓͚ΔϝϞϦ؅ཧ ͦ͏͸͍ͬͯ΋΍͸Γ GC ͸ศརɽGCͱϝϞ Ϧखಈ؅ཧͷؒͷଘࡏ͕΄͍͠ɽ → std::shared_ptr Q

  6. std::shared_ptr • Ϧιʔεͷॴ༗ݖΛࢀরΧ΢ϯτʹΑͬͯ؅ཧ͢ ΔϙΠϯλΫϥε • ϦιʔεΛॴ༗͍ͯ͠Δ shared_ptr ͷ਺ΛΧ΢ ϯτ͓͖ͯ͠ɼΧ΢ϯτ͕ 0

    ʹͳͬͨ࣌ʹϦ ιʔεͷσετϥΫλΛ࣮ߦ͢Δ • GC ͷΑ͏ʹࣗಈ؅ཧ͞ΕɼϦιʔεͷղ์λ Πϛϯά͕༧ଌՄೳͰ͋Δɽ Q
  7. { std::shared_ptr<int> p1{new Widget}; { auto p2 = p1; }

    } std::shared_ptr 8JEHFU  Q DPOTUSVDU DPOTUSVDU Q
  8. { std::shared_ptr<int> p1{new Widget}; { auto p2 = p1; }

    } std::shared_ptr 8JEHFU  Q Q DPOTUSVDU Q
  9. { std::shared_ptr<int> p1{new Widget}; { auto p2 = p1; }

    } std::shared_ptr 8JEHFU  Q Q EFTUSPZ Q
  10. { std::shared_ptr<int> p1{new Widget}; { auto p2 = p1; }

    } std::shared_ptr 8JEHFU  Q EFTUSPZ EFTUSPZ Q
  11. ࢀরΧ΢ϯτͷίετ • shared_ptr ͷαΠζ → ੜϙΠϯλͷ2ഒʢ࣮ ࡍʹ͸ίϯτϩʔϧϒϩοΫ΋ʢޙड़ʣʣ • ϝϞϦϦιʔε͸ಈతʹ֬อ͢Δඞཁ͕͋Δ •

    ࢀরΧ΢ϯτͷૢ࡞͸ atomic Ͱ͋Δ (thread safety) Q
  12. ࢀরΧ΢ϯτͷίετͷճආ • move ίϯετϥΫλΛ࢖͏ͱࢀরΧ΢ϯτ ͸มԽ͠ͳ͍ʢॴ༗ݖͷҠৡʣ ! ! • ʢ͜ͷଞʹ΋஋౉͠Ͱͳ͘ࢀর౉͠ʹ͢Δͳ ͲͰࢀরΧ΢ϯτͷ্ԼΛ๷͛Δʣ

    Q std::shared_ptr<int> p1{new int}; ! // ࢀরΧ΢ϯτ͸มԽͤͣɼp1 ʹ͸ null ͕୅ೖ͞ΕΔ std::shared_ptr<int> p2{ std::move(p1) };
  13. shared_ptr ͷσϦʔλ • unique_ptr ͱ͸ҧ͍ɼσϦʔλ͕ܕͷҰ෦Ͱ ͸ͳ͍ʢtype erasure ʹΑΔ࣮૷ʣ auto loggingDel

    = [](Widget *pw) { makeLogEntry(pw); delete pw; }; ! std::unique_ptr<Widget, decltype(loggingDel)> upw(new Widget, loggingDel); ! std::shared_ptr<Widget> spw(new Widget, loggingDel); Q
  14. shared_ptr ͷσϦʔλ • ҟͳΔσϦʔλΛ࣋ͭෳ਺ͷ shared_ptr Λ1 ͭͷίϯςφͰ؅ཧͨ͠Γɼޓ͍ʹ୅ೖͰ͖ ͨΓ͢Δ ! !

    ! • ݸਓతʹ͸͜ΕͰԿ͕خ͍͠ͷ͔Α͘෼͔Βͳ͍Ͱ͕͢ɼͲ͏ͤ control block ΍ࢀরΧ΢ϯτͳͲΛผ్ׂ Γ౰ͯΔͳΒ type erasure Ͱফ͠ͱ͚ͱ͍͏ײ͡ͳͷ͔ͳ… auto customDeleter1 = [](Widget *pw) { ... }; auto customDeleter2 = [](Widget *pw) { ... }; ! std::shared_ptr<Widget> pw1(new Widget, customDeleter1); std::shared_ptr<Widget> pw2(new Widget, customDeleter2); ! std::vector<std::shared_ptr<Widget>> vpw{ pw1, pw2 }; Q
  15. shared_ptr ͷαΠζ • unique_ptr ͱ͸ҟͳΓɼৗʹϙΠϯλ2ͭ෼ • ͋ΕɼͰ΋ΧελϜσϦʔλͷαΠζ͸…ʁ • → ΧελϜσϦʔλͳͲ͸ผͷ৔ॴʹ֬อ

    ͞ΕΔʢޙड़ʣ Q
  16. shared_ptr ͷ಺෦ߏ଄ 1PJOUFSUP5 1PJOUFSUP$POUSPM#MPDL 50CKFDU 3FGFSFODF$PVOU 8FBL$PVOU 0UIFS%BUB DVTUPNEFMFUFS 

    BMMPDBUPS FUD TIBSFE@QUS5 $POUSPM#MPDL 3FTPVSDF   Q
  17. Control Block • ࢀরΧ΢ϯτ΍ऑࢀরΧ΢ϯτͳͲͷ؅ཧ৘ ใΛ࣋ͭ • ؅ཧର৅ͷΦϒδΣΫτ͕ੜ੒͞Εͨ࣌ʹ1౓ ͚ͩੜ੒͞ΕΔ • Χ΢ϯτ͕྆ํ0ʹͳͬͨ࣌ʹഁغ͞ΕΔ

    Q
  18. Control Block ͷੜ੒λΠϛϯά • std::make_shared() ͕ݺ͹Εͨ࣌ • unique_ptr ΍ auto_ptr

    Λݩʹ shared_ptr ͕ ͭ͘ΒΕͨ࣌ • ੜϙΠϯλΛݩʹ shared_ptr ͕ͭ͘ΒΕͨ࣌ Q $POUSPM#MPDL͸Ϧιʔεʹର͚ͯͭͩ͠ੜ੒ ͠ͳ͚Ε͹ͳΒͳ͍ʢϓϩάϥϚͷ੹೚ʣ
  19. Control Block ͕ॏෳ͢Δྫ ಉ͡ੜϙΠϯλΛݩʹ2ճ shared_ptr Λੜ੒ ͢Δͱ Control Block ͕ॏෳͯ͠͠·͏

    auto pw = new Widget; ! std::shared_ptr<Widget> spw1(pw, loggingDel); // Control Block ੜ੒ ! std::shared_ptr<Widget> spw2(pw, loggingDel); // 2ͭ໨Λੜ੒ ! // spw1 ͱ spw2 ͸ͦΕͧΕผݸʹ pw Λ؅ཧͯ͠͠·͍ͬͯΔͷͰɼ // σϦʔλ͸2ճݺ͹Εͯ͠·͏ʢະఆٛಈ࡞ʣ Q
  20. Control Block ͕ॏෳ͢Δྫ • جຊతʹ͸ std::make_shared() Λ࢖͏΂͖ • ੜϙΠϯλ͔ΒͰ͸ͳ͘ shared_ptr

    ͔Β shared_ptr Λੜ੒͢Δ ! ! ! • ࠓճ͸ΧελϜΞϩέʔλΛࢦఆ͍ͯ͠ΔͷͰ make_shared ͸࢖͑ͳ͍ɽͳ͓ɼΞϩ έʔλΛࢦఆ͍ͨ͠৔߹͸ std::allocate_shared() ͕͋Δɽʢmake_shared_with_deleter() ͕΄͠ ͍ʣ auto pw = new Widget; ! std::shared_ptr<Widget> spw1(pw, loggingDel); // Control Block ੜ੒ ! std::shared_ptr<Widget> spw2(spw1); // OK Q
  21. Control Block ͕ॏෳ͢Δྫ this ͔Β shared_ptr Λੜ੒͢Δ৔߹ std::vector<std::shared_ptr<Widget>> processedWidgets; !

    class Widget{ public: … void process(); … }; ! void Widget::process() { … processedWidgets.emplace_back(this); } Q
  22. Control Block ͕ॏෳ͢Δྫ this ͸ੜϙΠϯλͳͷͰɼprocess() ͕ݺ͹Ε Δͨͼʹ৽͍͠ Control Block ͕ͭ͘ΒΕΔ

    // ຊʹ͸ॻ͍ͯ·ͤΜ͕ɼ͜͏͍͏͜ͱ͕΍Γ͍ͨ͸ͣ ! std::shared_ptr<Widget> spw = std::make_shared(…); ! spw->process(); // NG ผͷ Control Block ͕ͭ͘ΒΕͯ͠·͏ Q
  23. Control Block ͕ॏෳ͢Δྫ std::enable_shared_from_this Ϋϥεςϯϓ ϨʔτΛ࢖͏͜ͱͰղܾ Q // Curiously Recurring

    Template Pattern (CRTP) Λ࢖࣮ͬͯ૷͞Ε͍ͯΔ class Widget : public std::enable_shared_from_this<Widget> { … }; ! void Widget::process() { … processedWidgets.emplace_back(shared_from_this()); }
  24. std::enable_shared_from_this • ܧঝ͢Δͱϝϯόؔ਺ shared_from_this() ͕࢖͑ΔΑ͏ʹͳ ΔΫϥεςϯϓϨʔτ • shared_from_this() ͸ Control-Block

    Λͭ͘Βͣʹ shared_ptr Λ࡞੒͢Δ • Control-Block ͕ແ͍৔߹͸ະఆٛಈ࡞ʢ͍͍ͨͯ͸ྫ֎ʣ • Control-Block ͕طʹ࡞੒͞Ε͍ͯΔඞཁ͕͋Δʢଞͷ shared_ptr ͕ͦͷΦϒδΣΫτΛࢦ͍ͯ͠Δඞཁ͕͋Δʣ Q
  25. std::enable_shared_from_this enable_shared_from_this Λܧঝͨ͠Ϋϥε͸ ඞͣ shared_ptr Ͱ؅ཧ͞ΕΔΑ͏ʹϑΝΫτ Ϧؔ਺Λͭ͘Δ class Widget :

    public std::enable_shared_from_this<Widget> { public: template<typename… Ts> static std::shared_ptr<Widget> create(Ts &&… params) { return std::shared_ptr<Widget>{new Widget(std::forward<Ts>(params)…)}; } … ! private: … // ίϯετϥΫλఆٛ } Q
  26. shared_ptr ͷίετ͸ reasonable • Control Block ͷαΠζ͸σϑΥϧτͰ3word • Control Block

    ͸࣮૷ʹԾ૝ؔ਺Λ࢖͍ͬͯΔ ͕ݺ͹ΕΔͷ͸Ϧιʔε͕ഁغ͞ΕΔ1ճͷΈ • େ఍ͷϚγϯͰ͸ atomic ͳૢ࡞͸1໋ྩͰ࣮ ߦͰ͖Δ Q
  27. shared_ptr ͷίετ͸ reasonable • एׯͷίετͱҾ͖׵͑ʹण໋؅ཧΛࣗಈͰ ߦ͑Δ • ͨͩ͠ॴ༗ݖΛڞ༗͢Δඞཁ͕ແ͍ͳΒ unique_ptr ͷ΄͏͕ྑ͍

    • unique_ptr ͸ඞཁʹԠͯ͡ shared_ptr ʹ౉ ͤΔ Q
  28. ഑ྻͷ؅ཧ • C++14 ࣌఺Ͱ͸ shared_ptr<T []> ͸ߟྀ͞Ε͍ͯ ͳ͍ • std::vector

    ͳͲͷίϯςφΛ࢖͏΂͖ • C++17 Ͱ഑ྻͷαϙʔτ͕ఏҊ͞Ε͍ͯΔ ʢN3869, N3920, N3939ʣ • boost::shared_ptr ͸഑ྻରԠࡁΈ Q
  29. ഑ྻͷ؅ཧ • ΧελϜσϦʔλΛ࢖ͬͯ delete T[] ΛݺͿΑ ͏ʹͯ͠΋μϝ • operator[] ͕ແ͍

    • جఈΫϥεͷ shared_ptr ͕೿ੜΫϥεͷΦ ϒδΣΫτΛࢦ͢ͱ͖ɼ഑ྻΛߟྀ͍ͯ͠ ͳ͍ Q
  30. Things to Remember • shared_ptr ͸ෳ਺ͷϙΠϯλʹڞ༗͞ΕΔϝϞϦ΍೚ҙͷϦ ιʔεͷण໋؅ཧΛߦ͑Δ • unique_ptr ͱൺ΂ͯαΠζ΍

    atomic ͳૢ࡞ͳͲͷΦʔόʔϔο υ͕͋Δ • Ϧιʔεͷഁغʹ͸σϑΥϧτͰ delete ͕࢖ΘΕΔ͕ɼΧελ ϜσϦʔλ΋࢖͑ΔɽΧελϜσϦʔλ͸ shared_ptr ͷܕʹӨ ڹΛ༩͑ͳ͍ • ੜϙΠϯλม਺͔Β shared_ptr Λͭ͘Δ͜ͱ͸ۃྗආ͚Δ΂͖ Q
  31. Item20: ࢦ͢Ϧιʔε͕ແޮʹͳΓ ͏Δ shared_ptr ʹ͸ std::weak_ptr Λ࢖͓͏

  32. ॴ༗ݖΛ࣋ͨͳ͍ϙΠϯλ • shared_ptr ͷΑ͏ʹಛఆͷϦιʔεΛࢦͤΔ ͕ɼॴ༗ݖ͸࣋ͨͳ͍ʢ=ࢀরΧ΢ϯτΛಈ͔ ͞ͳ͍ʣεϚʔτϙΠϯλ͕΄͍͠ • ͦͷϙΠϯλ͸ࢦ͍ͯ͠ΔϦιʔε͕ଞͷ shared_ptr ʹ࡟আ͞ΕΔՄೳੑ͕͋Δ

    • ͦΕ std::weak_ptr ͰͰ͖ΔΑʂ Q
  33. std::weak_ptr • dereference ΋ null νΣοΫ΋Ͱ͖ͳ͍ • shared_ptr ΁ͷঢ֨ͱࢦ͍ͯ͠ΔϦιʔε͕ ੜ͖͍ͯΔ͔Ͳ͏͔ͷνΣοΫ͕Ͱ͖Δ

    • shared_ptr ͱҰॹʹ࢖͏ʢ͍͍ͨͯ shared_ptr ͔Βੜ੒͞ΕΔʣ Q
  34. auto spw = std::make_shared<Widget>(); ←ࠓίίʂ ! std::weak_ptr<Widget> wpw(spw); ! std::cout

    << wpw.expired() << std::endl; // false ! spw = nullptr; // Delete Widget object ! std::cout << wpw.expired() << std::endl; // true std::weak_ptr Q 8JEHFU  TQX
  35. auto spw = std::make_shared<Widget>(); ! std::weak_ptr<Widget> wpw(spw); ←ࠓίίʂ ! std::cout

    << wpw.expired() << std::endl; // false ! spw = nullptr; // Delete Widget object ! std::cout << wpw.expired() << std::endl; // true std::weak_ptr Q 8JEHFU  TQX XQX
  36. auto spw = std::make_shared<Widget>(); ! std::weak_ptr<Widget> wpw(spw); ! std::cout <<

    wpw.expired() << std::endl; // false ! spw = nullptr; // Delete Widget object ←ࠓίίʂ ! std::cout << wpw.expired() << std::endl; // true std::weak_ptr Q 8JEHFU  XQX
  37. Race condition auto spw = std::make_shared<Widget>(); ! std::weak_ptr<Widget> wpw(spw); !

    if (!wpw.expired()) { … // ͜͜ͰଞͷεϨουʹΑͬͯ spw ʹ nullptr ͕୅ೖ͞Εͯ͠·͏ … wpw.lock()->do_something(); // ະఆٛಈ࡞ } expired() ͷνΣοΫͷޙʹଞͷεϨουʹ ΑͬͯϦιʔε͕࡟আ͞Εͯ͠·͏ͷͰ͸ʁ Q
  38. Race condition auto spw = std::make_shared<Widget>(); std::weak_ptr<Widget> wpw(spw); ! //

    wpw ͕ expire ͍ͯ͠Δͱ͖͸ spw2 ͸ null ʹͳΔ auto spw2 = wpw.lock(); ! // wpw ͕ expire ͍ͯ͠Δͱ͖͸ std::bad_weak_ptr ͕౤͛ΒΕΔ std::shared_ptr<Widget> spw3(wpw); → weak_ptr::lock() ͔ shared_ptr ͷίϯετϥΫλΛ ࢖͏ Q ※ ݸਓతʹ͸ if (auto spw = wpw.lock()) { … ͷΑ͏ʹॻ͚Δ lock() ͷ΄͏͕Φεεϝ
  39. Ϣʔεέʔε1: Ωϟογϡ Item18ʹैͬͯ Widget Λੜ੒͠ unique_ptr Ͱฦ͢ϑΝΫτϦؔ਺Λ࡞੒ ! ! loadWidget()

    ͸ॲཧ͕ॏ͍ͷͰ Widget ʹৼ ΒΕͨ ID Λ࢖ͬͯΩϟογϡ͍ͨ͠ std::unique_ptr<const Widget> loadWidget(WidgetID id); Q
  40. std::unique_ptr<const Widget> loadWidget(WidgetID id); ! std::shared_ptr<const Widget> fastLoadWidget(WidgetID id) {

    static std::unordered_map<WidgetID, std::weak_ptr<const Widget>> cache; ! // Ωϟογϡ͕ແ͍͔ expire ͍ͯ͠Δ࣌ objPtr ͸ۭʹͳΔ auto objPtr = cache[id].lock(); ! // Ωϟογϡ͞Ε͍ͯͳ͚Ε͹৽͘͠ Widget Λͭ͘ΓɼΩϟογϡ͢Δ if (!objPtr) { objPtr = loadWidget(id); cache[id] = objPtr; } ! return objPtr; } Q Ϣʔεέʔε1: Ωϟογϡ
  41. Ϣʔεέʔε1: Ωϟογϡ • ੜ੒ͨ͠Ϧιʔε͕ഁغ͞Ε͍ͯΔ͔Ͳ͏͔Λ஌Δඞ ཁ͕͋ΔͨΊɼweak_ptr ͰΩϟογϡ͢Δͷ͕ద೚ • Ϧιʔεͷঢ়ଶΛ஌ΔͨΊʹΩϟογϡ෇͖ϑΝΫτ Ϧؔ਺ͷ໭Γ஋͸ shared_ptr

    ʹ͢Δඞཁ͕͋Δ (Control Block ಺ͷ weak count) • expire ͨ͠Ωϟογϡͷ࡟আͳͲΛ௥Ճͯ͠΋ྑ͍ ͔΋͠Εͳ͍ Q
  42. Ϣʔεέʔε2: Observer • observerʢsubject ͔Β௨஌Λड͚औΓԿ͔ॲཧΛ ߦ͏ʣ • subjectʢঢ়ଶΛ࣋ͪ observer ʹঢ়ଶͷมߋΛ௨஌

    ͢Δʣ • subject ͸ observer Λࢀর͢Δ͕ɼobserver ͸ࢮ͵ ͜ͱ͕͋Δ → subject ͸ observer Λ weak_ptr Ͱ࣋ ͭ΂͖ Q
  43. Ϣʔεέʔε2: Observer ͜Μͳײ͡ʁʢຊจதʹ͸ྫͳ͠ʣ class Subject { public: void register_observer(std::shared_ptr<Observer> const&

    o) { observers.emplace_back(o); } ! void update_state() { for (auto const& w : observers) { if (auto const o = w.lock()) { o->notify(…); } } } ! private: std::vector<std::weak_ptr<Observer>> observers; }; Q
  44. Ϣʔεέʔε3: σʔλߏ଄ ! ! ! B ͕ A Λࢦ࣌͢ɼԿΛ࢖͑͹ྑ͍͔ #

    " $ TIBSFE@QUS TIBSFE@QUS Q
  45. ??? ͷ෦෼ʹԿΛ࢖͑͹ྑ͍͔ • ੜϙΠϯλ → A ͕ഁغ͞Εͨ࣌ʹͦΕΛ஌Δํ๏͕ͳ͍ NG • shared_ptr

    → ॥؀ࢀরͯ͠͠·͍ɼӬԕʹࢀরΧ΢ϯτ ͕ 0 ʹͳΒͳ͘ͳΔʢϝϞϦϦʔΫʣ NG • weak_ptr → A ͕ഁغ͞Ε͍ͯΔ͔Ͳ͏͔Λ஌Δ͜ͱ͕ Ͱ͖ɼA Λॴ༗͍ͯ͠ͳ͍ͷͰ॥؀ࢀর΋ͳ͍ OK • ※໦ߏ଄Ͱ਌ͷΈ͕ࢠΛ shared_ptr Ͱࢦ͢ͱ͍ͬͨಛघͳ৚݅Ͱ͋Ε͹ࢠˠ਌͸ੜϙΠϯλͰ΋ྑ͍ Q
  46. weak_ptr ͷίετ • αΠζ͸ shared_ptr ͱಉ͡ʢshared_ptr ͱಉ ༷ʹ Control Block

    Λར༻͢Δʣ • Χ΢ϯτͷૢ࡞͸ atomic Ͱ͋Δ → ࢀরΧ΢ ϯτͰ͸ͳ͘ऑࢀরΧ΢ϯτΛૢ࡞͢Δ 3FGFSFODF$PVOU 8FBL$PVOU 0UIFS%BUB DVTUPNEFMFUFS BMMPDBUPS  FUD $POUSPM#MPDL • Control Block ͸͢΂ͯͷ weak_ptr ͕ ͍ͳ͘ͳͬͯॳΊͯ࡟আͰ͖ΔͷͰऑ ࢀরΛΧ΢ϯτ͢Δඞཁ͕͋ΔʢItem 21ʣ Q
  47. Things to Remember • Ϧιʔε͕ແޮʹͳΔ͜ͱ͕͋Δ shared_ptr ͷΑ͏ͳϙΠϯλ͕΄͍࣌͠͸ std::weak_ptr Λ࢖͏ •

    weak_ptr ͷओͳϢʔεέʔε͸Ωϟογϡɼ Φϒβʔόɼ॥؀ࢀরͷճආͳͲ Q