shared_ptr & weak_ptr (pdf 第2版)
Upcoming SlideShare
Loading in...5
×
 

shared_ptr & weak_ptr (pdf 第2版)

on

  • 10,968 views

2009/12/12 Boost.勉強会 プレゼン資料 ppt 第2版.

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

Statistics

Views

Total Views
10,968
Views on SlideShare
10,906
Embed Views
62

Actions

Likes
12
Downloads
104
Comments
0

7 Embeds 62

http://www.slideshare.net 47
http://yamabato.cocolog-nifty.com 8
http://b.hatena.ne.jp 3
https://twitter.com 1
http://blog.naver.com 1
https://twimg0-a.akamaihd.net 1
https://si0.twimg.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

shared_ptr & weak_ptr (pdf 第2版) shared_ptr & weak_ptr (pdf 第2版) Presentation Transcript

  • shared_ptr & weak_ptr くらいおらいと http://twitter.com/Cryolite/ 1 2009/12/14
  • キーワードは所有 (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
  • 所有とは… • 所有とは権利だ 俺が所有しているものを勝⼿に捨てるな • 所有とは義務だ 1回だけ,確実に,事後処理をしろ 3 2009/12/14
  • 所有を制するものが C++ を制する • 所有の権利を主張しないと… 「俺がまだ使っているのに捨てられた!」 => Dangling pointer • 所有の義務を果たしていないと… 「誰も使ってないのに⽚付いていない!」 => Memory leak 4 2009/12/14
  • 所有の種類と例 • 所有者がただ⼀⼈, 所有者変更不可 • 配列とその要素 • クラスオブジェクトとメンバ変数 • 関数スコープ (の実⾏) と⾃動変数 • プログラム (の実⾏) と静的変数 • 所有者がただ⼀⼈, 所有者変更可 • std::auto_ptr • (ムーブセマンティクス) • 共有 – 所有者が複数 • boost::shared_ptr 5 2009/12/14
  • 共有は難しい 6 2009/12/14
  • 共有は難しい 私が⽚付けましょう 7 2009/12/14
  • 共有は難しい いえいえ,ここは 私が⽚付けましょう 私が⽚付けましょう 8 2009/12/14
  • 共有は難しい いえいえ,ここは 私が⽚付けましょう 私が⽚付けましょう え,ちょ,俺まだ使ってる 9 2009/12/14
  • shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 1 10 2009/12/14
  • shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 2 11 2009/12/14
  • shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 3 12 2009/12/14
  • shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 4 13 2009/12/14
  • shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウントが0になったら 所有カウント 片づけを実行 4 14 2009/12/14
  • 所有カウントは所有の権利を保障する 所有の権利: 誰かが所有していれば 共有されるもの 勝手に片付けるな O.K. 所有カウント 1 15 2009/12/14
  • 所有カウントは所有の義務を履行する 所有カウントが0になれば 後片付け処理を実行 共有されるもの 所有の義務: 所有カウント 1度だけ, 確実に, 片付け 0 O.K. 16 2009/12/14
  • shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 4 17 2009/12/14
  • shared_ptr – 共有を所有カウントで簡単・安全に扱う 共有されるもの 所有カウント 4 shared_ptr shared_ptr shared_ptr shared_ptr 18 2009/12/14
  • shared_ptr は所有(の義務と権利)とポインタだ • ポインタとして動作する shared_ptr C++ の生ポインタと同じように動作 • 所有の権利を保障する shared_ptr ポインタとして指しているものは生きている 強いポインタ 所有しているものとポインタが 指しているものを一致させる • 所有の義務を履行する shared_ptr 所有カウントが0になったら関数オブジェクト (デリータ) を実行 delete だけじゃない 19 2009/12/14
  • 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
  • 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
  • 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
  • shared_ptr – int と同程度にスレッド安全 共有されるもの 所有カウント カウントの変化は 4 同期保護 shared_ptr shared_ptr shared_ptr shared_ptr 23 2009/12/14
  • 所有カウントは循環所有を扱えない 1 所有 所有 1 所有カウントが永遠に非0 24 2009/12/14
  • 発表終わり 25 2009/12/14
  • shared_ptr の重要なデザインゴール • ポインタと同等の型互換性 / バイナリ互換性 • 非侵入性 – あなたのクラス定義に変更なし 所有の共有・受け渡しを表現する 所有の共有・受け渡しを表現する 標準インタフェイスの確立 標準インタフェイスの確立 26 2009/12/14
  • オレオレスマートポインタ void processX(shared_ptr<X> px); oreore_ptr<X> px(new X(…)); processX(px); // コンパイルエラー 27 2009/12/14
  • オレオレスマートポインタ 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
  • オレオレスマートポインタ 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
  • 所有しない 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
  • 所有しない 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
  • バイナリ境界を越える – 生ポインタの場合 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
  • バイナリ境界を越える – 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
  • バイナリ境界を越える – 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
  • バイナリ境界を越える – 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
  • バイナリ境界を越える – 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
  • 所有だけの shared_ptr shared_ptr<int> p(new int(42)); // (A) shared_ptr<void> q = p; p.reset(); // 以下, (A) で生成した int は q が所有 37 2009/12/14
  • shared_ptr<void> による遅延解放 // HeavyToDispose は削除のコスト大 shared_ptr<HeavyToDispose> px(…); … // ここで削除して処理が止まると困る… px.reset(); 38 2009/12/14
  • 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
  • weak_ptr 共有されるもの 所有カウント 弱いカウント 4, 2 weak_ptr shared_ptr shared_ptr weak_ptr shared_ptr shared_ptr 40 2009/12/14
  • weak_ptr 共有されるもの 所有カウント 弱いカウント 0, 2 weak_ptr shared_ptr shared_ptr weak_ptr shared_ptr shared_ptr 41 2009/12/14
  • weak_ptr 所有カウントが0になったら 共有されるもの 片づけを実行 所有カウント 弱いカウント 0, 2 weak_ptr weak_ptr 42 2009/12/14
  • weak_ptr 所有カウント 弱いカウント 0, 2 weak_ptr weak_ptr 43 2009/12/14
  • weak_ptr 所有カウント 弱いカウント 0, 0 weak_ptr weak_ptr 44 2009/12/14
  • weak_ptr 0, 0 弱いカウントが0になったら カウントオブジェクトを片付け weak_ptr weak_ptr 45 2009/12/14
  • 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
  • weak_ptr にできること – その1 所有カウント 弱いカウント 0, 2 「所有カウントが0かどうか?」に答える weak_ptr weak_ptr 47 weak_ptr::expired == true 2009/12/14
  • weak_ptr にできること – その1 所有カウント 弱いカウント 0, 2 「所有カウントが0かどうか?」に答える = weak_ptr 「対象が死んでいるかどうか?」に答える weak_ptr 48 2009/12/14
  • weak_ptr にできること – その2 共有されるもの 所有カウント 弱いカウント 1, 2 shared_ptr weak_ptr weak_ptr 対象が生きていたら,それを 所有する shared_ptr を作り出せる 49 2009/12/14
  • weak_ptr にできること – その2 共有されるもの 所有カウント 弱いカウント 2, 2 shared_ptr weak_ptr shared_ptr weak_ptr 対象が生きていたら,それを 所有する shared_ptr を作り出せる 50 2009/12/14
  • weak_ptr にできること – その2 所有カウント 弱いカウント 0, 2 weak_ptr weak_ptr 対象が死んでいたら空の shared_ptr しか取り出せない 51 2009/12/14
  • weak_ptr にできること – その2 所有カウント 弱いカウント 0, 2 weak_ptr shared_ptr weak_ptr 対象が死んでいたら空の shared_ptr しか取り出せない 52 2009/12/14
  • weak_ptr にできることのまとめ • 「対象が生きているかどうか?」を答えるプロ クシ • 対象が生きていたら shared_ptr に格上げ可 能 53 2009/12/14
  • 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
  • 生ポインタから shared_ptr を取得したい void Framework::onEvent(X *p) { // *p を所有する shared_ptr を取りたい } 55 2009/12/14
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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); shared_ptr<Subscriber1> p… Publisher::subscribe( bind(&Subscriber1::notifyEvent, p.get()), p); 65 2009/12/14
  • 安全な Observer weak_ptr<void> wp…; // subscriber への weak_ptr if (shared_ptr<void> p = wp.lock()) { // 生きているときだけ通知 + call_back 中だけ所有 call_back(); } 66 2009/12/14
  • 安全な Observer …ということが Boost.Signal2 で出来ます 鍵は weak_ptr<void> & shared_ptr<void> 67 2009/12/14
  • オブジェクト間グローバルマッピング share_ptr a share_ptr b c share_ptr share_ptr 68 2009/12/14
  • オブジェクト間グローバルマッピング share_ptr map<void *, Y> g_map; // グローバル a y1 share_ptr b y2 c y3 share_ptr share_ptr 69 2009/12/14
  • オブジェクト間グローバルマッピング share_ptr map<void *, Y> g_map; // グローバル a y1 share_ptr b y2 c y3 c が消えると… share_ptr share_ptr 70 2009/12/14
  • オブジェクト間グローバルマッピング share_ptr map<void *, Y> g_map; // グローバル a y1 share_ptr b y2 c y3 c が消えると… 無駄なマップエントリ (デッドマップ) が発生 share_ptr share_ptr 71 2009/12/14
  • 策1. キーを weak_ptr にして適度にクリーンアップ share_ptr map<weak_ptr<void>, Y> g_map; a y1 share_ptr b y2 c y3 72 2009/12/14
  • 策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
  • 策1. キーを weak_ptr にして適度にクリーンアップ share_ptr map<weak_ptr<void>, Y> g_map; a y1 share_ptr b y2 c y3 デッドマップが適度に解消される 74 2009/12/14
  • 策2. マップエントリも所有する share_ptr map<void *, Y> a y1 share_ptr b y2 c y3 share_ptr share_ptr 75 2009/12/14
  • 策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
  • 策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
  • 策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
  • 循環所有を何とかする // こういうことがしたい 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
  • 循環所有を何とかする // こういうことがしたい 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
  • 循環所有を何とかする // こういうことがしたい 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
  • 循環所有を何とかする // こういうことがしたい 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
  • 循環所有を何とかする // こういうことがしたい 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
  • まとめ • C++ では所有が重要 • shared_ptr は所有者が複数居る場合に – 所有カウントで簡単・安全に動作 – 動的デリータによる高い互換性を維持 • weak_ptr – 「生きているかどうか」を答えるプロキシ – shared_ptr に格上げして一時的に所有 • shared_ptr / weak_ptr で楽しい C++ ライフ 84 2009/12/14