3.3.11. pfi::lang::shared_ptr

3.3.11.1. 概要

スマートポインタ。 C++11のshared_ptr - deleter 相当の機能を持つ。

(deleterは将来的に実装する予定)

3.3.11.2. 基本的な使い方

3.3.11.2.1. shared_ptr<T, TM>

T型へのポインタを表す。

TMにはスレッディングモデルを指定するが、これは後方互換性維持のために残されているだけで、 デフォルトではpfi::concurrent::threading_model::single_threadが指定される。 そのためユーザーが明示的にテンプレート引数を渡す必要は無い。

shared_ptrは普通のコピーセマンティクスを持つので、 普通のポインタとして用いることのできるところならどこでも使える (vectorの要素にしたりなど)。

shared_ptr<T> p(new T());

shared_ptrのコンストラクタにポインタを渡すと、 そのポインタはshared_ptrによって管理されることになる。 auto_ptrなどと違い、一度shared_ptrに入れられると、 そのポインタはshared_ptrによる管理から外すことはできない。 これはshared_ptrが普通のコピーセマンティクスを持つことによる制限である。

非参照

*p

ポインタの取得

p.get()

ポインタ風のメンバへのアクセス

p->hoge

NULLをアサインする。 持っていたポインタは必要に応じて解放される。

p.reset()

ポインタ風にboolテストができる。

if (p)

3.3.11.2.2. その他のメンバ関数

大体boost互換である。

3.3.11.2.3. 注意

参照カウント方式なので、循環参照されていると解放できない。 現在のところ、weak_ptrなどは提供していないため、 回避するにはデストラクタで明示的に参照を外してやるなどの処理が必要である。

3.3.11.2.4. なぜこれが重要なのか

リソースをその取得と同時にリソース管理オブジェクトに渡すリソース管理方法はRAIIと呼ばれる。 RAIIが重要な理由は次のような理由がある。

  • リークしないという保証を得るため

手でdeleteするプログラムは、当然ながらdeleteを忘れるとメモリリークする。 shared_ptrを使っていれば、イージーミスによるメモリリークは起こり得ない。 何も考えずともメモリリークが起こらないことが保証されていることと、 気をつけなければいつでもメモリリークを起こしうるということは かなり異なるので、deleteの付け忘れに気を付ければ良い、というものではない。

また、制御構造によっては、本質的にdeleteを正確に入れるのが困難な場合もある。 そういう場合、コードはgotoとif文が乱れ飛ぶ難解なものになりがちである。 shared_ptrで管理すれば、何も考える必要はない。

  • 例外安全なコードをシンプルに書くため

例外がどこで飛ぶか分からない状況では(例えば、任意の型を取るテンプレート関数など)、 RAIIを用いずに例外安全なコードを書くのは大変困難である。 例外が起こりうる場所をすべて把握し、すべてのコードパスを考慮し、 正確にリソース開放の処理を入れるのは事実上不可能である。 RAIIであれば、何も考えずともデストラクタが宜しくやってくれる。