shared_ptr & weak_ptr くらいおらいと http://twitter.com/Cryolite/
キーワードは 所有  ( 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
所有とは… 所有とは 権利 だ 俺が所有しているものを勝手に捨てるな 所有とは 義務 だ 1 回だけ,確実に,事後処理をしろ
所有を制するものが  C++  を制する 所有の権利を主張しないと… 「俺がまだ使っているのに捨てられた!」 =>  Dangling pointer 所有の義務を果たしていないと… 「誰も使ってないのに片付いていない!」 =>  Memory Leak
所有の種類と例 所有者がただ一人 ,  所有者変更不可 配列とその要素 クラスオブジェクトとメンバ変数 関数スコープ  ( の実行 )  と自動変数 プログラム  ( の実行 )  と静的変数 所有者がただ一人 ,  所有者変更可 std::auto_ptr ( ムーブセマンティクス ) 共有  – 所有者が複数 boost::shared_ptr
共有は難しい
共有は難しい 私が片付けましょう
共有は難しい 私が片付けましょう いえいえ,ここは 私が片付けましょう
共有は難しい 私が片付けましょう いえいえ,ここは 私が片付けましょう え,ちょ,俺まだ使ってる
shared_ptr –  共有を 所有カウント で簡単・安全に扱う 1 共有されるもの 所有カウント
shared_ptr –  共有を 所有カウント で簡単・安全に扱う 2 共有されるもの 所有カウント
shared_ptr –  共有を 所有カウント で簡単・安全に扱う 3 共有されるもの 所有カウント
shared_ptr –  共有を 所有カウント で簡単・安全に扱う 4 共有されるもの 所有カウント
shared_ptr –  共有を 所有カウント で簡単・安全に扱う 4 共有されるもの 所有カウント 所有カウントが 0 になったら 片づけを実行
所有カウントは所有の 権利を保障 する 1 共有されるもの 所有カウント 所有の権利: 誰かが所有していれば 勝手に片付けるな O.K.
所有カウントは所有の 義務を履行 する 0 共有されるもの 所有カウントが0になれば後片付け処理を実行 所有の義務: 1度だけ, 確実に, 片付け 所有カウント O.K.
shared_ptr –  共有を 所有カウント で簡単・安全に扱う 4 共有されるもの 所有カウント
shared_ptr –  共有を 所有カウント で簡単・安全に扱う 4 共有されるもの 所有カウント shared_ptr shared_ptr shared_ptr shared_ptr
shared_ptr  は所有 ( の義務と権利 ) とポインタだ ポインタとして動作する  shared_ptr C++  の生ポインタと同じように動作 所有の権利を保障する  shared_ptr ポインタとして指しているものは生きている 所有の義務を履行する  shared_ptr 所有カウントが 0 になったら関数オブジェクト  ( デリータ )  を実行 強いポインタ 所有しているものとポインタが 指しているものを一致させる delete  だけじゃない
shared_ptr  の基本 { shared_ptr<int> p(new int(42)); cout << *p << endl; shared_ptr<int> q = p; p.reset(); cout << *q << endl; }
shared_ptr  の基本 { shared_ptr<int> p(new int(42)); cout << *p << endl; shared_ptr<int> q = p; p.reset(); cout << *q << endl; } ポインタとして動作 + 権利を保障  ( 参照外しが安全 )
shared_ptr  の基本 { shared_ptr<int> p(new int(42)); cout << *p << endl; shared_ptr<int> q = p; p.reset(); cout << *q << endl; } ポインタとして動作 + 権利を保障  ( 参照外しが安全 ) 義務を履行
shared_ptr – int  と同程度にスレッド安全 4 共有されるもの 所有カウント カウントの変化は 同期保護 shared_ptr shared_ptr shared_ptr shared_ptr
所有カウントは循環所有を扱えない 1 1 所有 所有 所有カウントが永遠に非 0
発表終わり
shared_ptr  の重要なデザインゴール ポインタと同等の型互換性 / バイナリ互換性 非侵入性 – あなたのクラス定義に変更なし 所有の共有・受け渡しを表現する 標準インタフェイスの確立
オレオレスマートポインタ void processX(shared_ptr<X> px); oreore_ptr<X> px(new X(…)); processX(px); //  コンパイルエラー
オレオレスマートポインタ 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);
オレオレスマートポインタ 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); 「所有」は型に現れない = コンストラクタで「所有」が決まる shared_ptr  の 「所有」は型に現れない ポイント 2 ポイント 1
所有しない void processX(shared_ptr<X> px); static X x; //  静的変数 struct NullDeleter { void operator()(void *){} }; shared_ptr<X> px(&x, NullDeleter()); processX(px);
所有しない void processX(shared_ptr<X> px); static X x; //  静的変数 Struct NullDeleter { void operator()(void *){} }; shared_ptr<X> px(&x, NullDeleter()); processX(px); shared_ptr<X> px2(new X()); processX(px2); 「所有」は型に現れない = コンストラクタで「所有」が決まる shared_ptr  の 「所有」は型に現れない ポイント 2 ポイント 1
バイナリ境界を越える int main(){ int *p = new int(42); f(p); p = 0; ..... } void f(int *p){ ..... ..... delete p; } a.exe ( デバッグビルド ) b.dll ( リリースビルド ) 異なるコンパイル設定の  new  と  delete  の 組み合わせは環境によってはダウト
バイナリ境界を越える int main(){ shared_ptr<int> p(new int(42)); f(p); p.reset(); ..... } void f(shared_ptr<int> p){ ..... ..... p.reset(); } a.exe ( デバッグビルド ) b.dll ( リリースビルド )
バイナリ境界を越える int main(){ shared_ptr<int> p(new int(42)); f(p); p.reset(); ..... } void f(shared_ptr<int> p){ ..... ..... p.reset(); } a.exe ( デバッグビルド ) b.dll ( リリースビルド ) デバッグビルドの  delete  をここで設定
バイナリ境界を越える int main(){ shared_ptr<int> p(new int(42)); f(p); p.reset(); ..... } void f(shared_ptr<int> p){ ..... ..... p.reset(); } a.exe ( デバッグビルド ) b.dll ( リリースビルド ) デバッグビルドの  delete  をここで設定 デバッグビルドの  delete  が呼び出される
バイナリ境界を越える int main(){ shared_ptr<int> p(new int(42)); f(p); p.reset(); ..... } void f(shared_ptr<int> p){ ..... ..... p.reset(); } a.exe ( デバッグビルド ) b.dll ( リリースビルド ) デバッグビルドの  delete  が呼び出される デバッグビルドの  delete  をここで設定 どの  delete  が設定されていようが バイナリ互換性を維持
所有だけの  shared_ptr shared_ptr<int> p(new int(42)); //  (A) shared_ptr<void> q = p; p.reset(); //  以下,  (A)   で生成した  int  は  q  が所有
shared_ptr<void>  による遅延解放 // HeavyToDispose  は削除のコスト大 shared_ptr<HeavyToDispose> px(…); … //  ここで削除して処理が止まると困る… px.reset();
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()  を実行
weak_ptr 4,  2 共有されるもの 所有カウント shared_ptr shared_ptr shared_ptr shared_ptr weak_ptr weak_ptr 弱いカウント
weak_ptr 0,  2 共有されるもの 所有カウント shared_ptr shared_ptr shared_ptr shared_ptr weak_ptr weak_ptr 弱いカウント
weak_ptr 0,  2 共有されるもの 所有カウント weak_ptr weak_ptr 弱いカウント 所有カウントが 0 になったら 片づけを実行
weak_ptr 0,  2 所有カウント weak_ptr weak_ptr 弱いカウント
weak_ptr 0,  0 所有カウント weak_ptr weak_ptr 弱いカウント
weak_ptr 0,   0 weak_ptr weak_ptr 弱いカウントが 0 になったら カウントオブジェクトを片付け
weak_ptr  にできること – その 1 4,  2 共有されるもの 所有カウント shared_ptr shared_ptr shared_ptr shared_ptr weak_ptr weak_ptr 弱いカウント 「所有カウントが 0 かどうか?」に答える weak_ptr::expired == false
weak_ptr  にできること – その 1 0,  2 所有カウント weak_ptr weak_ptr 弱いカウント 「所有カウントが 0 かどうか?」に答える weak_ptr::expired == true
weak_ptr  にできること – その 1 0,  2 所有カウント weak_ptr weak_ptr 弱いカウント 「所有カウントが 0 かどうか?」に答える = 「対象が死んでいるかどうか?」に答える
weak_ptr  にできること – その 2 1,  2 共有されるもの 所有カウント shared_ptr weak_ptr weak_ptr 弱いカウント 対象が生きていたら,それを 所有する  shared_ptr  を作り出せる
weak_ptr  にできること – その 2 2,  2 共有されるもの 所有カウント shared_ptr weak_ptr weak_ptr 弱いカウント shared_ptr 対象が生きていたら,それを 所有する  shared_ptr  を作り出せる
weak_ptr  にできること – その 2 0,  2 所有カウント weak_ptr weak_ptr 弱いカウント 対象が死んでいたら空の  shared_ptr  しか取り出せない
weak_ptr  にできること – その 2 0,  2 所有カウント weak_ptr weak_ptr 弱いカウント shared_ptr 対象が死んでいたら空の  shared_ptr  しか取り出せない
weak_ptr  にできることのまとめ 「対象が生きているかどうか?」を答える プロクシ 対象が生きていたら  shared_ptr  に 格上げ可能
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);
生ポインタから  shared_ptr  を取得したい void Framework::onEvent(X *p) { // *p  を所有する  shared_ptr  を取りたい }
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(); }
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(); }
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(); } X X::shared_ptr this_ クラスオブジェクトは メンバ変数を所有 所有 Q.  なぜダメか? A.  所有が循環しているのでダメ
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(); }
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
Observer (Publisher/Subscriber)
Observer でよくある事故
Observer でよくある事故 死んだオブジェクトにアクセス
安全な Observer 死んだオブジェクトにイベントを通知しない イベント通知中に  subscriber  を解放させない
安全な Observer void Publisher::subscribe(function<void ()> call_back, weak_ptr<void> wp); shared_ptr<Subscriber1> p… Publisher::subscribe( bind(&Subscriber1::notifyEvent, p.get()), p);
安全な Observer weak_ptr<void> wp…; // subscriber  への  weak_ptr if (shared_ptr<void> p = wp.lock()) { call_back(); }
安全な Observer … ということが  Boost.Signal2  で出来ます 鍵は  weak_ptr<void> & shared_ptr<void>
オブジェクト間グローバルマッピング a b c share_ptr share_ptr share_ptr share_ptr
オブジェクト間グローバルマッピング a b c map<void *, Y> g_map; //  グローバル y1 y2 y3 share_ptr share_ptr share_ptr share_ptr
オブジェクト間グローバルマッピング a b c y1 y2 y3 share_ptr share_ptr share_ptr share_ptr c  が消えると… map<void *, Y> g_map; //  グローバル
オブジェクト間グローバルマッピング a b c y1 y2 y3 share_ptr share_ptr share_ptr share_ptr c  が消えると… 無駄なマップエントリ ( デッドマップ )  が発生 map<void *, Y> g_map; //  グローバル
策 1.  キーを  weak_ptr  にして適度にクリーンアップ a b c map< weak_ptr<void> , Y> g_map; y1 y2 y3 share_ptr share_ptr
策 1.  キーを  weak_ptr  にして適度にクリーンアップ a b c map< weak_ptr<void> , Y> g_map; y1 y2 y3 share_ptr share_ptr //  以下を適度に実行 for ( auto  i = g_map.begin(); i != g_map.end();) { if (i->first.expired()) g_map.erase(i++); else ++i; }
策 1.  キーを  weak_ptr  にして適度にクリーンアップ a b c map< weak_ptr<void> , Y> g_map; y1 y2 y3 share_ptr share_ptr デッドマップが適度に解消される
策2. マップエントリも所有する a b c y1 y2 y3 share_ptr share_ptr share_ptr share_ptr map<void *, Y>
策2. マップエントリも所有する a b c y1 y2 y3 share_ptr share_ptr share_ptr share_ptr map<void *, Y> struct D{ map<void *, Y> &m_; void operator()(void *p){ m_.erase(p); delete p; } }; D d(g_map); shared_ptr<C> pc(new C(), D(g_map)); g_map.insert(make_pair(pc.get(), Y(…)));
策2. マップエントリも所有する a b c map<void *, Y> y1 y2 y3 share_ptr share_ptr share_ptr share_ptr c  を所有する  shared_ptr  がなくなると…
策2. マップエントリも所有する a b c unordered_map<void const *, Y> y1 y2 y3 share_ptr share_ptr share_ptr share_ptr c  を所有する  shared_ptr  がなくなると… c  をキーとするエントリも自動で  erase
循環所有を何とかする //  こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; pyy = px->getSharedY(); // X  が  Y  を所有? assert(pyy == py); pxx = py->getSharedX(); // Y  が  X  を所有? assert(pxx == px);
循環所有を何とかする //  こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; pyy = px->getSharedY(); // X  が  Y  を所有? assert(pyy == py); pxx = py->getSharedX(); // Y  が  X  を所有? assert(pxx == px); X Y 所有 所有 ?
循環所有を何とかする //  こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; pyy = px->getSharedY(); // X  が  Y  を所有? assert(pyy == py); pxx = py->getSharedX(); // Y  が  X  を所有? assert(pxx == px); X Y shared_ptr 所有 所有
循環所有を何とかする //  こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; pyy = px->getSharedY(); // X  が  Y  を所有? assert(pyy == py); pxx = py->getSharedX(); // Y  が  X  を所有? assert(pxx == px); X Y shared_ptr 所有 所有 ポイント shared_ptr<X>
循環所有を何とかする //  こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; pyy = px->getSharedY(); // X  が  Y  を所有? assert(pyy == py); pxx = py->getSharedX(); // Y  が  X  を所有? assert(pxx == px); X Y shared_ptr 所有 所有 shared_ptr<Y> ポイント
循環所有を何とかする //  こういうことがしたい shared_ptr<X> px = …; shared_ptr<Y> py = …; pyy = px->getSharedY(); // X  が  Y  を所有? assert(pyy == py); pxx = py->getSharedX(); // Y  が  X  を所有? assert(pxx == px); X Y shared_ptr 所有 所有 shared_ptr<Y> ポイント
まとめ C++  では所有が重要 shared_ptr  は所有者が複数居る場合に 所有カウントで簡単・安全に動作 動的デリータによる高い互換性を維持 weak_ptr 「生きているかどうか」を答えるプロキシ shared_ptr  に格上げして一時的に所有 shared_ptr / weak_ptr  で楽しい  C++  ライフ

shared_ptr & weak_ptr (ppt 初版, DL 専用)

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