shared_ptr & weak_ptr
               くらいおらいと
        http://twitter.com/Cryolite/




1                                   ...
キーワードは所有 (ownership)
    • “…, so keeping track of an object’s
      ownership is hard work.”    – More Effective C++
    ...
所有とは…



    • 所有とは権利だ
      俺が所有しているものを勝⼿に捨てるな



    • 所有とは義務だ
      1回だけ,確実に,事後処理をしろ



3                        2009/1...
所有を制するものが C++ を制する



    • 所有の権利を主張しないと…
      「俺がまだ使っているのに捨てられた!」
                => Dangling pointer

    • 所有の義務を果たしてい...
所有の種類と例
    • 所有者がただ⼀⼈, 所有者変更不可
      • 配列とその要素
      • クラスオブジェクトとメンバ変数
      • 関数スコープ (の実⾏) と⾃動変数
      • プログラム (の実⾏) と静的...
共有は難しい




6        2009/12/14
共有は難しい



    私が⽚付けましょう




7               2009/12/14
共有は難しい

                 いえいえ,ここは
    私が⽚付けましょう   私が⽚付けましょう




8                       2009/12/14
共有は難しい

                 いえいえ,ここは
    私が⽚付けましょう   私が⽚付けましょう




       え,ちょ,俺まだ使ってる
9                       2009/12/14
shared_ptr – 共有を所有カウントで簡単・安全に扱う


              共有されるもの

                        所有カウント

                 1




10        ...
shared_ptr – 共有を所有カウントで簡単・安全に扱う


              共有されるもの

                        所有カウント

                 2




11        ...
shared_ptr – 共有を所有カウントで簡単・安全に扱う


              共有されるもの

                        所有カウント

                 3




12        ...
shared_ptr – 共有を所有カウントで簡単・安全に扱う


              共有されるもの

                        所有カウント

                 4




13        ...
shared_ptr – 共有を所有カウントで簡単・安全に扱う


              共有されるもの

 所有カウントが0になったら          所有カウント
    片づけを実行
                 4




...
所有カウントは所有の権利を保障する

     所有の権利:
     誰かが所有していれば    共有されるもの
     勝手に片付けるな
            O.K.             所有カウント

             ...
所有カウントは所有の義務を履行する
     所有カウントが0になれば
      後片付け処理を実行
                       共有されるもの

     所有の義務:                      所有カウン...
shared_ptr – 共有を所有カウントで簡単・安全に扱う


              共有されるもの

                        所有カウント

                 4




17        ...
shared_ptr – 共有を所有カウントで簡単・安全に扱う


                         共有されるもの

                                    所有カウント

          ...
shared_ptr は所有(の義務と権利)とポインタだ
     • ポインタとして動作する shared_ptr
       C++ の生ポインタと同じように動作
     • 所有の権利を保障する shared_ptr
       ポ...
shared_ptr の基本
     {
         shared_ptr<int> p(new int(42));
         cout << *p << endl;
         shared_ptr<int> q = p...
shared_ptr の基本
     {
         shared_ptr<int> p(new int(42));
         cout << *p << endl;
         shared_ptr<int> q = p...
shared_ptr の基本
     {
         shared_ptr<int> p(new int(42));
         cout << *p << endl;
         shared_ptr<int> q = p...
shared_ptr – int と同程度にスレッド安全


                         共有されるもの

                                    所有カウント
              ...
所有カウントは循環所有を扱えない



              1

         所有
                  所有

              1

      所有カウントが永遠に非0

24            ...
発表終わり




25      2009/12/14
shared_ptr の重要なデザインゴール



     • ポインタと同等の型互換性 / バイナリ互換性

     • 非侵入性 – あなたのクラス定義に変更なし


         所有の共有・受け渡しを表現する
         ...
オレオレスマートポインタ
     void processX(shared_ptr<X> px);

     oreore_ptr<X> px(new X(…));
     processX(px); // コンパイルエラー




27...
オレオレスマートポインタ
     void processX(shared_ptr<X> px);

     template<class P> struct D {
        P p_;
        void operator(...
オレオレスマートポインタ
     void processX(shared_ptr<X> px);

     template<class P> struct D {
                ポイント1
        P p_;
...
所有しない
     void processX(shared_ptr<X> px);

     static X x; // 静的変数

     struct NullDeleter {
        void operator()(v...
所有しない
     void processX(shared_ptr<X> px);

     static X x; // 静的変数
           ポイント1

     Structshared_ptr の「所有」は型に現れない...
バイナリ境界を越える – 生ポインタの場合
      a.exe (デバッグビルド)     b.dll (リリースビルド)
int main(){
  int *p = new int(42);
  f(p);               ...
バイナリ境界を越える – shared_ptr の場合
      a.exe (デバッグビルド)               b.dll (リリースビルド)
int main(){
  shared_ptr<int> p(new int(42...
バイナリ境界を越える – shared_ptr の場合
      a.exe (デバッグビルド)               b.dll (リリースビルド)
int main(){
  shared_ptr<int> p(new int(42...
バイナリ境界を越える – shared_ptr の場合
      a.exe (デバッグビルド)               b.dll (リリースビルド)
int main(){
  shared_ptr<int> p(new int(42...
バイナリ境界を越える – shared_ptr の場合
      a.exe (デバッグビルド)               b.dll (リリースビルド)
int main(){
  shared_ptr<int> p(new int(42...
所有だけの shared_ptr
     shared_ptr<int> p(new int(42)); // (A)

     shared_ptr<void> q = p;
     p.reset();
     // 以下, (A)...
shared_ptr<void> による遅延解放
     // HeavyToDispose は削除のコスト大
     shared_ptr<HeavyToDispose> px(…);
     …
     // ここで削除して処理が止...
shared_ptr<void> による遅延解放
     vector<shared_ptr<void> > to_be_disposed;

     shared_ptr<HeavyToDispose1> px(…);
     shar...
weak_ptr

                                  共有されるもの


                  所有カウント
                                           ...
weak_ptr

                                  共有されるもの


                  所有カウント
                                           ...
weak_ptr
 所有カウントが0になったら
                    共有されるもの
    片づけを実行

           所有カウント
                              弱いカウント
   ...
weak_ptr




           所有カウント
                           弱いカウント
                    0, 2


                             w...
weak_ptr




           所有カウント
                           弱いカウント
                    0, 0


                             w...
weak_ptr




                      0, 0
      弱いカウントが0になったら
     カウントオブジェクトを片付け
                               weak_ptr
  ...
weak_ptr にできること – その1

                                  共有されるもの


                  所有カウント
                              ...
weak_ptr にできること – その1




          所有カウント
                                     弱いカウント
                            0, 2

 ...
weak_ptr にできること – その1




           所有カウント
                              弱いカウント
                       0, 2

      「所有カウン...
weak_ptr にできること – その2

                   共有されるもの


          所有カウント
                               弱いカウント
               ...
weak_ptr にできること – その2

                         共有されるもの


          所有カウント
                                   弱いカウント
     ...
weak_ptr にできること – その2




         所有カウント
                               弱いカウント
                    0, 2


               ...
weak_ptr にできること – その2




         所有カウント
                               弱いカウント
                       0, 2


            ...
weak_ptr にできることのまとめ

     • 「対象が生きているかどうか?」を答えるプロ
       クシ

     • 対象が生きていたら shared_ptr に格上げ可
       能




53            ...
weak_ptr の基本
     shared_ptr<int> p(new int(42)); // (A)
     weak_ptr<int> wp = p;
     cout << wp.expired() << endl; // ...
生ポインタから shared_ptr を取得したい
     void Framework::onEvent(X *p)
     {
       // *p を所有する shared_ptr を取りたい
     }




55     ...
this への shared_ptr
      struct X {
         shared_ptr<X> this_; // this への shared_ptr
         shared_ptr<X> getShared()...
this への shared_ptr はダメ
     struct X {
        shared_ptr<X> this_; // this への shared_ptr
        shared_ptr<X> getShared(...
this への shared_ptr はダメ
     struct X {
        Q. なぜダメか?
        shared_ptr<X> this_; // this への shared_ptr
        shared...
weak_ptr の使い方 – this への weak_ptr
     struct X {
        weak_ptr<X> this_; // this への weak_ptr
        shared_ptr<X> getS...
weak_ptr の使い方 – this への weak_ptr
     struct X {
        weak_ptr<X> this_; // this への weak_ptr
        shared_ptr<X> getS...
Observer (Publisher/Subscriber)




61                                2009/12/14
Observer でよくある事故




62                 2009/12/14
Observer でよくある事故




       死んだオブジェクトにアクセス
63                      2009/12/14
安全な Observer




     • 死んだオブジェクトにイベントを通知しない
     • イベント通知中に subscriber を解放させない
64                               2009/12/14
安全な Observer




void Publisher::subscribe(function<void ()> call_back,
                          weak_ptr<void> wp);

sha...
安全な Observer




weak_ptr<void> wp…; // subscriber への weak_ptr
if (shared_ptr<void> p = wp.lock()) {
   // 生きているときだけ通知 + c...
安全な Observer




     …ということが Boost.Signal2 で出来ます
     鍵は weak_ptr<void> & shared_ptr<void>
67                            ...
オブジェクト間グローバルマッピング
 share_ptr

             a
 share_ptr   b
             c




 share_ptr   share_ptr

68                 ...
オブジェクト間グローバルマッピング
 share_ptr    map<void *, Y> g_map; // グローバル

             a                        y1
 share_ptr   b   ...
オブジェクト間グローバルマッピング
 share_ptr    map<void *, Y> g_map; // グローバル

             a                        y1
 share_ptr   b   ...
オブジェクト間グローバルマッピング
 share_ptr    map<void *, Y> g_map; // グローバル

             a                        y1
 share_ptr   b   ...
策1. キーを weak_ptr にして適度にクリーンアップ
 share_ptr       map<weak_ptr<void>, Y> g_map;

             a                            y...
策1. キーを weak_ptr にして適度にクリーンアップ
 share_ptr         map<weak_ptr<void>, Y> g_map;

               a                         ...
策1. キーを weak_ptr にして適度にクリーンアップ
 share_ptr       map<weak_ptr<void>, Y> g_map;

             a                            y...
策2. マップエントリも所有する
 share_ptr               map<void *, Y>

             a                            y1
 share_ptr   b     ...
策2. マップエントリも所有する
 share_ptr                     map<void *, Y>

                 a
           struct D{                   ...
策2. マップエントリも所有する
 share_ptr               map<void *, Y>

             a                            y1
 share_ptr   b     ...
策2. マップエントリも所有する
 share_ptr               map<void *, Y>

             a                            y1
 share_ptr   b     ...
循環所有を何とかする
     // こういうことがしたい
     shared_ptr<X> px = …;
     shared_ptr<Y> py = …;

     pyy = px->getSharedY(); // X が Y...
循環所有を何とかする
     // こういうことがしたい
     shared_ptr<X> px = …;
     shared_ptr<Y> py = …;

                        所有
     pyy =...
循環所有を何とかする
     // こういうことがしたい
     shared_ptr<X> px = …;
     shared_ptr<Y> py = …;
                    shared_ptr
       ...
循環所有を何とかする
     // こういうことがしたい
     shared_ptr<X> px = …;
     shared_ptr<Y> py = …;
                     shared_ptr
      ...
循環所有を何とかする
     // こういうことがしたい
     shared_ptr<X> px = …;
     shared_ptr<Y> py = …;
                    shared_ptr   ポイント
...
まとめ
      • C++ では所有が重要
      • shared_ptr は所有者が複数居る場合に
        – 所有カウントで簡単・安全に動作
        – 動的デリータによる高い互換性を維持
      • weak...
Upcoming SlideShare
Loading in …5
×

shared_ptr & weak_ptr (pdf 第2版)

10,081 views

Published on

2009/12/12 Boost.勉強会 プレゼン資料 ppt 第2版.
初版の明らかな不手際を最低限だけ修正.
プレゼン発表の録画は http://bit.ly/6yjSkz です.

Published in: Technology
0 Comments
12 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
10,081
On SlideShare
0
From Embeds
0
Number of Embeds
143
Actions
Shares
0
Downloads
112
Comments
0
Likes
12
Embeds 0
No embeds

No notes for slide

shared_ptr & weak_ptr (pdf 第2版)

  1. 1. shared_ptr & weak_ptr くらいおらいと http://twitter.com/Cryolite/ 1 2009/12/14
  2. 2. キーワードは所有 (ownership) • “…, so keeping track of an object’s ownership is hard work.” – More Effective C++ • “Observe the canonical exception-safety rules: Always use the “RAII” idiom to resource ownership and management.” – Exceptional C++ • “Containers of raw pointers make manual ownership management tricky, so …” – Modern C++ Design 2 2009/12/14
  3. 3. 所有とは… • 所有とは権利だ 俺が所有しているものを勝⼿に捨てるな • 所有とは義務だ 1回だけ,確実に,事後処理をしろ 3 2009/12/14
  4. 4. 所有を制するものが C++ を制する • 所有の権利を主張しないと… 「俺がまだ使っているのに捨てられた!」 => Dangling pointer • 所有の義務を果たしていないと… 「誰も使ってないのに⽚付いていない!」 => Memory leak 4 2009/12/14
  5. 5. 所有の種類と例 • 所有者がただ⼀⼈, 所有者変更不可 • 配列とその要素 • クラスオブジェクトとメンバ変数 • 関数スコープ (の実⾏) と⾃動変数 • プログラム (の実⾏) と静的変数 • 所有者がただ⼀⼈, 所有者変更可 • std::auto_ptr • (ムーブセマンティクス) • 共有 – 所有者が複数 • boost::shared_ptr 5 2009/12/14
  6. 6. 共有は難しい 6 2009/12/14
  7. 7. 共有は難しい 私が⽚付けましょう 7 2009/12/14
  8. 8. 共有は難しい いえいえ,ここは 私が⽚付けましょう 私が⽚付けましょう 8 2009/12/14
  9. 9. 共有は難しい いえいえ,ここは 私が⽚付けましょう 私が⽚付けましょう え,ちょ,俺まだ使ってる 9 2009/12/14
  10. 10. shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 1 10 2009/12/14
  11. 11. shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 2 11 2009/12/14
  12. 12. shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 3 12 2009/12/14
  13. 13. shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 4 13 2009/12/14
  14. 14. shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウントが0になったら 所有カウント 片づけを実行 4 14 2009/12/14
  15. 15. 所有カウントは所有の権利を保障する 所有の権利: 誰かが所有していれば 共有されるもの 勝手に片付けるな O.K. 所有カウント 1 15 2009/12/14
  16. 16. 所有カウントは所有の義務を履行する 所有カウントが0になれば 後片付け処理を実行 共有されるもの 所有の義務: 所有カウント 1度だけ, 確実に, 片付け 0 O.K. 16 2009/12/14
  17. 17. shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 4 17 2009/12/14
  18. 18. shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 4 shared_ptr shared_ptr shared_ptr shared_ptr 18 2009/12/14
  19. 19. shared_ptr は所有(の義務と権利)とポインタだ • ポインタとして動作する shared_ptr C++ の生ポインタと同じように動作 • 所有の権利を保障する shared_ptr ポインタとして指しているものは生きている 強いポインタ 所有しているものとポインタが 指しているものを一致させる • 所有の義務を履行する shared_ptr 所有カウントが0になったら関数オブジェクト (デリータ) を実行 delete だけじゃない 19 2009/12/14
  20. 20. shared_ptr の基本 { shared_ptr<int> p(new int(42)); cout << *p << endl; shared_ptr<int> q = p; p.reset(); cout << *q << endl; } 20 2009/12/14
  21. 21. shared_ptr の基本 { shared_ptr<int> p(new int(42)); cout << *p << endl; shared_ptr<int> q = p; p.reset(); cout << *q << endl; } ポインタとして動作 + 権利を保障 (参照外しが安全) 21 2009/12/14
  22. 22. shared_ptr の基本 { shared_ptr<int> p(new int(42)); cout << *p << endl; shared_ptr<int> q = p; p.reset(); cout << *q << endl; } ポインタとして動作 + 義務を履行 権利を保障 (参照外しが安全) 22 2009/12/14
  23. 23. shared_ptr – int と同程度にスレッド安全 共有されるもの 所有カウント カウントの変化は 4 同期保護 shared_ptr shared_ptr shared_ptr shared_ptr 23 2009/12/14
  24. 24. 所有カウントは循環所有を扱えない 1 所有 所有 1 所有カウントが永遠に非0 24 2009/12/14
  25. 25. 発表終わり 25 2009/12/14
  26. 26. shared_ptr の重要なデザインゴール • ポインタと同等の型互換性 / バイナリ互換性 • 非侵入性 – あなたのクラス定義に変更なし 所有の共有・受け渡しを表現する 所有の共有・受け渡しを表現する 標準インタフェイスの確立 標準インタフェイスの確立 26 2009/12/14
  27. 27. オレオレスマートポインタ void processX(shared_ptr<X> px); oreore_ptr<X> px(new X(…)); processX(px); // コンパイルエラー 27 2009/12/14
  28. 28. オレオレスマートポインタ void processX(shared_ptr<X> px); template<class P> struct D { P p_; void operator()(void const *) { p_.reset(); } }; oreore_ptr<X> px(new X(…)); shared_ptr<X> qx(px.get(), D<oreore_ptr<X> >(px)); processX(qx); // O.K. 28 2009/12/14
  29. 29. オレオレスマートポインタ void processX(shared_ptr<X> px); template<class P> struct D { ポイント1 P p_; shared_ptr の「所有」は型に現れない void operator()(void const *) { p_.reset(); } }; ポイント2 oreore_ptr<X> px(new X(…)); 「所有」は型に現れない shared_ptr<X> qx(px.get(), = D<oreore_ptr<X> >(px)); コンストラクタで「所有」が決まる processX(qx); 29 2009/12/14
  30. 30. 所有しない void processX(shared_ptr<X> px); static X x; // 静的変数 struct NullDeleter { void operator()(void *){} }; shared_ptr<X> px(&x, NullDeleter()); processX(px); 30 2009/12/14
  31. 31. 所有しない void processX(shared_ptr<X> px); static X x; // 静的変数 ポイント1 Structshared_ptr の「所有」は型に現れない NullDeleter { void operator()(void *){} }; ポイント2 「所有」は型に現れない = shared_ptr<X> px(&x, NullDeleter()); コンストラクタで「所有」が決まる processX(px); 31 2009/12/14
  32. 32. バイナリ境界を越える – 生ポインタの場合 a.exe (デバッグビルド) b.dll (リリースビルド) int main(){ int *p = new int(42); f(p); void f(int *p){ p = 0; ..... ..... ..... } delete p; } 異なるコンパイル設定の new と delete の 組み合わせは環境によってはダウト 32 2009/12/14
  33. 33. バイナリ境界を越える – shared_ptr の場合 a.exe (デバッグビルド) b.dll (リリースビルド) int main(){ shared_ptr<int> p(new int(42)); f(p); void f(shared_ptr<int> p){ p.reset(); ..... ..... ..... } p.reset(); } 33 2009/12/14
  34. 34. バイナリ境界を越える – shared_ptr の場合 a.exe (デバッグビルド) b.dll (リリースビルド) int main(){ shared_ptr<int> p(new int(42)); f(p); void f(shared_ptr<int> p){ p.reset(); ..... ..... ..... } p.reset(); } デバッグビルドの delete をここで設定 34 2009/12/14
  35. 35. バイナリ境界を越える – shared_ptr の場合 a.exe (デバッグビルド) b.dll (リリースビルド) int main(){ shared_ptr<int> p(new int(42)); f(p); void f(shared_ptr<int> p){ p.reset(); ..... ..... ..... } p.reset(); } デバッグビルドの delete をここで設定 デバッグビルドの delete が呼び出される 35 2009/12/14
  36. 36. バイナリ境界を越える – shared_ptr の場合 a.exe (デバッグビルド) b.dll (リリースビルド) int main(){ shared_ptr<int> p(new int(42)); f(p); void f(shared_ptr<int> p){ p.reset(); ..... ..... ..... } どの delete が設定されていようがp.reset(); バイナリ互換性を維持 } デバッグビルドの delete をここで設定 デバッグビルドの delete が呼び出される 36 2009/12/14
  37. 37. 所有だけの shared_ptr shared_ptr<int> p(new int(42)); // (A) shared_ptr<void> q = p; p.reset(); // 以下, (A) で生成した int は q が所有 37 2009/12/14
  38. 38. shared_ptr<void> による遅延解放 // HeavyToDispose は削除のコスト大 shared_ptr<HeavyToDispose> px(…); … // ここで削除して処理が止まると困る… px.reset(); 38 2009/12/14
  39. 39. shared_ptr<void> による遅延解放 vector<shared_ptr<void> > to_be_disposed; shared_ptr<HeavyToDispose1> px(…); shared_ptr<HeavyToDispose2> py(…); … // ここで削除して処理が止まると困る… to_be_disposed.push_back(px); px.reset(); to_be_disposed.push_back(py); py.reset(); … // 適当なタイミング or 別スレッドで // to_be_disposed.clear() を実行 39 2009/12/14
  40. 40. weak_ptr 共有されるもの 所有カウント 弱いカウント 4, 2 weak_ptr shared_ptr shared_ptr weak_ptr shared_ptr shared_ptr 40 2009/12/14
  41. 41. weak_ptr 共有されるもの 所有カウント 弱いカウント 0, 2 weak_ptr shared_ptr shared_ptr weak_ptr shared_ptr shared_ptr 41 2009/12/14
  42. 42. weak_ptr 所有カウントが0になったら 共有されるもの 片づけを実行 所有カウント 弱いカウント 0, 2 weak_ptr weak_ptr 42 2009/12/14
  43. 43. weak_ptr 所有カウント 弱いカウント 0, 2 weak_ptr weak_ptr 43 2009/12/14
  44. 44. weak_ptr 所有カウント 弱いカウント 0, 0 weak_ptr weak_ptr 44 2009/12/14
  45. 45. weak_ptr 0, 0 弱いカウントが0になったら カウントオブジェクトを片付け weak_ptr weak_ptr 45 2009/12/14
  46. 46. weak_ptr にできること – その1 共有されるもの 所有カウント 弱いカウント 4, 2 「所有カウントが0かどうか?」に答える weak_ptr shared_ptr shared_ptr weak_ptr shared_ptr shared_ptr 46 weak_ptr::expired == false 2009/12/14
  47. 47. weak_ptr にできること – その1 所有カウント 弱いカウント 0, 2 「所有カウントが0かどうか?」に答える weak_ptr weak_ptr 47 weak_ptr::expired == true 2009/12/14
  48. 48. weak_ptr にできること – その1 所有カウント 弱いカウント 0, 2 「所有カウントが0かどうか?」に答える = weak_ptr 「対象が死んでいるかどうか?」に答える weak_ptr 48 2009/12/14
  49. 49. weak_ptr にできること – その2 共有されるもの 所有カウント 弱いカウント 1, 2 shared_ptr weak_ptr weak_ptr 対象が生きていたら,それを 所有する shared_ptr を作り出せる 49 2009/12/14
  50. 50. weak_ptr にできること – その2 共有されるもの 所有カウント 弱いカウント 2, 2 shared_ptr weak_ptr shared_ptr weak_ptr 対象が生きていたら,それを 所有する shared_ptr を作り出せる 50 2009/12/14
  51. 51. weak_ptr にできること – その2 所有カウント 弱いカウント 0, 2 weak_ptr weak_ptr 対象が死んでいたら空の shared_ptr しか取り出せない 51 2009/12/14
  52. 52. weak_ptr にできること – その2 所有カウント 弱いカウント 0, 2 weak_ptr shared_ptr weak_ptr 対象が死んでいたら空の shared_ptr しか取り出せない 52 2009/12/14
  53. 53. weak_ptr にできることのまとめ • 「対象が生きているかどうか?」を答えるプロ クシ • 対象が生きていたら shared_ptr に格上げ可 能 53 2009/12/14
  54. 54. weak_ptr の基本 shared_ptr<int> p(new int(42)); // (A) weak_ptr<int> wp = p; cout << wp.expired() << endl; // => “false” shared_ptr<int> q = wp.lock(); cout << *q << endl; // => “42”, q も所有 p.reset(); q.reset(); // (A) の int を解放 cout << wp.expired() << endl; // => “true” shared_ptr<int> r = wp.lock(); assert(r == 0); 54 2009/12/14
  55. 55. 生ポインタから shared_ptr を取得したい void Framework::onEvent(X *p) { // *p を所有する shared_ptr を取りたい } 55 2009/12/14
  56. 56. this への shared_ptr struct X { shared_ptr<X> this_; // this への shared_ptr shared_ptr<X> getShared(){ return this_; } }; void Framework::onEvent(X *p) { shared_ptr<X> px = p->getShared(); } 56 2009/12/14
  57. 57. this への shared_ptr はダメ struct X { shared_ptr<X> this_; // this への shared_ptr shared_ptr<X> getShared(){ return this_; } }; void Framework::onEvent(X *p) { shared_ptr<X> px = p->getShared(); } 57 2009/12/14
  58. 58. this への shared_ptr はダメ struct X { Q. なぜダメか? shared_ptr<X> this_; // this への shared_ptr shared_ptr<X> getShared(){ return this_; } X }; クラスオブジェクトは 所有 メンバ変数を所有 void Framework::onEvent(X *p) { X::shared_ptr this_ shared_ptr<X> px = p->getShared(); } A. 所有が循環しているのでダメ 58 2009/12/14
  59. 59. weak_ptr の使い方 – this への weak_ptr struct X { weak_ptr<X> this_; // this への weak_ptr shared_ptr<X> getShared(){ return this_; } }; void Framework::onEvent(X *p) { shared_ptr<X> px = p->getShared(); } 59 2009/12/14
  60. 60. weak_ptr の使い方 – this への weak_ptr struct X { weak_ptr<X> this_; // this への weak_ptr shared_ptr<X> getShared(){ return this_; } }; void Framework::onEvent(X *p) { shared_ptr<X> px = p->getShared(); } 参考: boost::enable_shared_from_this 60 2009/12/14
  61. 61. Observer (Publisher/Subscriber) 61 2009/12/14
  62. 62. Observer でよくある事故 62 2009/12/14
  63. 63. Observer でよくある事故 死んだオブジェクトにアクセス 63 2009/12/14
  64. 64. 安全な Observer • 死んだオブジェクトにイベントを通知しない • イベント通知中に subscriber を解放させない 64 2009/12/14
  65. 65. 安全な Observer void Publisher::subscribe(function<void ()> call_back, weak_ptr<void> wp); shared_ptr<Subscriber1> p… Publisher::subscribe( bind(&Subscriber1::notifyEvent, p.get()), p); 65 2009/12/14
  66. 66. 安全な Observer weak_ptr<void> wp…; // subscriber への weak_ptr if (shared_ptr<void> p = wp.lock()) { // 生きているときだけ通知 + call_back 中だけ所有 call_back(); } 66 2009/12/14
  67. 67. 安全な Observer …ということが Boost.Signal2 で出来ます 鍵は weak_ptr<void> & shared_ptr<void> 67 2009/12/14
  68. 68. オブジェクト間グローバルマッピング share_ptr a share_ptr b c share_ptr share_ptr 68 2009/12/14
  69. 69. オブジェクト間グローバルマッピング share_ptr map<void *, Y> g_map; // グローバル a y1 share_ptr b y2 c y3 share_ptr share_ptr 69 2009/12/14
  70. 70. オブジェクト間グローバルマッピング share_ptr map<void *, Y> g_map; // グローバル a y1 share_ptr b y2 c y3 c が消えると… share_ptr share_ptr 70 2009/12/14
  71. 71. オブジェクト間グローバルマッピング share_ptr map<void *, Y> g_map; // グローバル a y1 share_ptr b y2 c y3 c が消えると… 無駄なマップエントリ (デッドマップ) が発生 share_ptr share_ptr 71 2009/12/14
  72. 72. 策1. キーを weak_ptr にして適度にクリーンアップ share_ptr map<weak_ptr<void>, Y> g_map; a y1 share_ptr b y2 c y3 72 2009/12/14
  73. 73. 策1. キーを weak_ptr にして適度にクリーンアップ share_ptr map<weak_ptr<void>, Y> g_map; a y1 share_ptr b y2 c y3 // 以下を適度に実行 for (auto i = g_map.begin(); i != g_map.end();) { if (i->first.expired()) g_map.erase(i++); else ++i; } 73 2009/12/14
  74. 74. 策1. キーを weak_ptr にして適度にクリーンアップ share_ptr map<weak_ptr<void>, Y> g_map; a y1 share_ptr b y2 c y3 デッドマップが適度に解消される 74 2009/12/14
  75. 75. 策2. マップエントリも所有する share_ptr map<void *, Y> a y1 share_ptr b y2 c y3 share_ptr share_ptr 75 2009/12/14
  76. 76. 策2. マップエントリも所有する share_ptr map<void *, Y> a struct D{ y1 map<void *, Y> &m_; share_ptr b y2 void operator()(void *p){ m_.erase(p); delete p; c y3 } }; D d(g_map); shared_ptr<C> pc(new C(), D(g_map)); share_ptrg_map.insert(make_pair(pc.get(), Y(…))); share_ptr 76 2009/12/14
  77. 77. 策2. マップエントリも所有する share_ptr map<void *, Y> a y1 share_ptr b y2 c y3 c を所有する shared_ptr がなくなると… share_ptr share_ptr 77 2009/12/14
  78. 78. 策2. マップエントリも所有する share_ptr map<void *, Y> a y1 share_ptr b y2 c y3 c をキーとするエントリも自動で erase c を所有する shared_ptr がなくなると… share_ptr share_ptr 78 2009/12/14
  79. 79. 循環所有を何とかする // こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; pyy = px->getSharedY(); // X が Y を所有? assert(pyy == py); pxx = py->getSharedX(); // Y が X を所有? assert(pxx == px); 79 2009/12/14
  80. 80. 循環所有を何とかする // こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; 所有 pyy = px->getSharedY(); // X が Y を所有? assert(pyy == py); X ? Y pxx = py->getSharedX(); // Y が X を所有? 所有 assert(pxx == px); 80 2009/12/14
  81. 81. 循環所有を何とかする // こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; shared_ptr 所有 所有 pyy = px->getSharedY(); // X が Y を所有? assert(pyy == py); X Y pxx = py->getSharedX(); // Y が X を所有? assert(pxx == px); 81 2009/12/14
  82. 82. 循環所有を何とかする // こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; shared_ptr ポイント 所有 所有 pyy = px->getSharedY(); // X が Y を所有? assert(pyy == py); X Y pxx = py->getSharedX(); // Y が X を所有? shared_ptr<X> assert(pxx == px); 82 2009/12/14
  83. 83. 循環所有を何とかする // こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; shared_ptr ポイント 所有 所有 pyy = px->getSharedY(); // X が Y を所有? assert(pyy == py); X Y pxx = py->getSharedX(); // Y が X を所有? shared_ptr<Y> assert(pxx == px); 83 2009/12/14
  84. 84. まとめ • C++ では所有が重要 • shared_ptr は所有者が複数居る場合に – 所有カウントで簡単・安全に動作 – 動的デリータによる高い互換性を維持 • weak_ptr – 「生きているかどうか」を答えるプロキシ – shared_ptr に格上げして一時的に所有 • shared_ptr / weak_ptr で楽しい C++ ライフ 84 2009/12/14

×