c++boost.gif (8819 bytes)shared_ptr クラステンプレート

はじめに
ベストプラクティス
概要
メンバ
非メンバ関数

Handle/Body イディオム
スレッド安全性
よくある質問
Smart Pointer Timings

はじめに

shared_ptr クラステンプレートは、動的に (典型的には C++ の new 式 によって) 確保されたオブジェクトへのポインタを格納します。 指し示されているオブジェクトは、それを指し示している最後の shared_ptr が破棄されるか初期化された時に解放 (delete) されます。を参照してください。

どの shared_ptr も、コピーコンストラクト可能かつ代入可能という C++ 標準ライブラリの要件を満たします。 したがって、標準ライブラリコンテナの中で使用することができます。 shared_ptr が標準ライブラリの連想コンテナに対しても動作するように、比較オペレータが提供されています。

普通、shared_ptr は動的に確保された配列へのポインタを正しく保持することができません。 配列での利用については、shared_array を参照してください。

参照カウントを用いた実装がなされているので、 shared_ptr インスタンスの循環があると再生されないでしょう。 たとえば、main()A に対する shared_ptr を保持しており、 A が自分自身に戻る shared_ptr を直接または間接的に保持しているとすると、A の使用カウントは 2 になるでしょう。 始点の shared_ptr を破棄すると、A は 使用カウント 1 を持ったまま宙ぶらりん状態になってしまいます。 「巡回を壊す」には weak_ptr を使用してください。

shared_ptr クラステンプレートは、指し示されているオブジェクトの型である T によってパラメータ化されます。 shared_ptr およびそのメンバ関数のほとんどは、T について何も要件を仮定しません。 不完全な型、すなわち void であっても構いません。 追加要件を必要とするメンバ関数 (コンストラクタ初期化) については、 後ほど明示的に説明します。

T* が暗黙裡に U* に変換されうる場合はいつでも、 shared_ptr<T> は、暗黙裡に shared_ptr<U> に変換されます。 shared_ptr<T> はとくに、shared_ptr<T const>shared_ptr<U> (UT のアクセス可能な基底のとき)、 および shared_ptr<void> への暗黙の変換が可能です。

ベストプラクティス

メモリリークの可能性をほとんど除去するための単純なガイドラインは次のとおりです: new の結果を保持するのに、つねに名前の付いたスマートポインタを使うこと。 new キーワードがコード中に出現する時は、いつも以下の形式であるべきです:

shared_ptr<T> p(new Y);

もちろん、上の shared_ptr のかわりに他のスマートポインタを使っても構いません。 ??? TY とを同じ型に強制したり、 Y のコンストラクタに引数を渡したりすることも OK です。???

このガイドラインをよく見てみると、 明示的な delete が不要であることが自然に導かれるでしょう。 try/catch 構造をとることは稀になるでしょう。

タイプ量を減らしたいがために名前のない一時的な shared_ptr オブジェクトを使うことは避けてください。これがなぜ危険であるかを見るのに、 次の例を考えてください:

void f(shared_ptr<int>, int);
int g();

void ok()
{
    shared_ptr<int> p(new int(2));
    f(p, g());
}

void bad()
{
    f(shared_ptr<int>(new int(2)), g());
}

関数 ok がガイドラインに忠実に従っているのに対して、 bad は同じところで一時的な shared_ptr を作成しています。これはメモリリークをまねきます。 関数引数を評価する順序は不定なので、new int(2) が最初に評価され、 次に g() が評価されるということがありえます。 そこでもし g が例外を送出すると、 shared_ptr のコンストラクタまで到達することができなくなってしまいます。 より詳しくは、この問題における Herb Sutter の対処 を参照してください。

概要

namespace boost {

  class use_count_is_zero: public std::exception;

  template<typename T> class weak_ptr;

  template<typename T> class shared_ptr {

    public:

      typedef T element_type;

      shared_ptr();
      template<typename Y> explicit shared_ptr(Y * p);
      template<typename Y, typename D> shared_ptr(Y * p, D d);
      ~shared_ptr(); // never throws

      shared_ptr(shared_ptr const & r); // never throws
      template<typename Y> shared_ptr(shared_ptr<Y> const & r); // never throws
      template<typename Y> explicit shared_ptr(weak_ptr<Y> const & r);
      template<typename Y> explicit shared_ptr(std::auto_ptr<Y> & r);

      shared_ptr & operator=(shared_ptr const & r); // never throws  
      template<typename Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws
      template<typename Y> shared_ptr & operator=(std::auto_ptr<Y> & r);

      void reset();
      template<typename Y> void reset(Y * p);
      template<typename Y, typename D> void reset(Y * p, D d);

      T & operator*() const; // never throws
      T * operator->() const; // never throws
      T * get() const; // never throws

      bool unique() const; // never throws
      long use_count() const; // never throws

      operator unspecified-bool-type() const; // never throws

      void swap(shared_ptr & b); // never throws
  };

  template<typename T, typename U>
    bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
  template<typename T, typename U>
    bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws
  template<typename T>
    bool operator<(shared_ptr<T> const & a, shared_ptr<T> const & b); // never throws

  template<typename T> void swap(shared_ptr<T> & a, shared_ptr<T> & b); // never throws

  template<typename T> T * get_pointer(shared_ptr<T> const & p); // never throws

  template<typename T, typename U>
    shared_ptr<T> shared_static_cast(shared_ptr<U> const & r); // never throws
  template<typename T, typename U>
    shared_ptr<T> shared_dynamic_cast(shared_ptr<U> const & r);
  template<typename T, typename U>
    shared_ptr<T> shared_polymorphic_cast(shared_ptr<U> const & r);
  template<typename T, typename U>
    shared_ptr<T> shared_polymorphic_downcast(shared_ptr<U> const & r); // never throws

}

[shared_ptr のシグネチャの要件を緩めて 付加的なデフォルト値の与えられたテンプレート引数を許すようにすると便利かもしれません。 その引数で、たとえばスレッドモデルを指示することができるでしょう。 これは、起こりうる ODR (one definition rule) 違反を検査するのにも役立つことでしょう。

他方、テンプレート型のテンプレート仮引数に渡す実引数として shared_ptr を使うにはシグネチャの厳密な一致が要求されます。 メタプログラミングのエキスパートは、 テンプレート型のテンプレート引数はあまりにも柔軟性に欠けるとして軽んじる傾向がありますが、 その代替物というのはえてして std::allocator::rebind-type "hack" であったりするのです。]

メンバ

要素型

typedef T element_type;

テンプレート引数 T の型を提供する。

コンストラクタ

shared_ptr();

結果: shared_ptr を構築。

事後条件: 使用カウント が 1; 格納ポインタが 0。

例外送出: std::bad_alloc

例外安全性: 例外が送出された場合、コンストラクタは何も作用を及ぼさない。

[use_count() == 1 という事後条件は強すぎます。 reset() がデフォルトコンストラクタに基づいて記述されているので、 例外を送出しないという保証は重要ですが、 現行の仕様はカウントが割り当てられることを要求しています。 それ故、この事後条件は将来のリリースでは無くなるでしょう。 デフォルトコンストラクタで構築された shared_ptr の使用カウント (それから生成されるすべてのコピーを含む) は、たぶん不定のままに置かれるでしょう。

例外送出のない2つの実装があります。 一つは参照カウントへのポインタとして 0 を格納するもの。 もう一つはデフォルトコンストラクタで構築されるすべての shared_ptr に対して唯一の静的に確保されたカウントを使用するものです。 2番目の選択肢では、スレッド安全性の問題と初期化の順序のせいで、 現行のヘッダーのみによるリファレンス実装で達成するのが難しくなりますが、 仕様によって除外されるべきではありません。

将来のリリースでは、組み込みのポインタとの一慣性のために、 リテラルの 0 から shared_ptr を構築することを可能にするかもしれません。 今のところは、このコンストラクタが implicit であり、 shared_ptr<T>() に対する略記法として 0 を使えるようにするべきかどうかについては、 明らかではありません。]

template<typename Y> explicit shared_ptr(Y * p);

要件: pT * に変換可能。 Y は完全な型。式 delete p は適格 (well-formed) であり、 未定義な振る舞いを引きおこしたり例外を送出したりしてはならない。

結果: shared_ptr を構築。p のコピーが格納される。

事後条件: 使用カウントが 1

例外送出: std::bad_alloc

例外安全性: 例外が送出された場合、 delete p が呼ばれる。

備考: p は C++ の new 式によって確保されたオブジェクトへのポインタか、 または 0 でなければならない。使用カウント が 1 という事後条件は、 p が 0 の場合でさえ適用される; 値が 0 であるポインタに対して delete を呼び出すことは無害である。

[このコンストラクタは、渡されたポインタの実際の型を憶えておくために、 テンプレートに変更されました。T が仮想デストラクタを持たなかったり、 あるいは void 型であったりしても、 デストラクタは、元の完全な型を持つポインタとして delete を呼ぶでしょう。

現行の実装では、pcounted_base * に変換可能であれば、shared_ptrcounted_base の提供する埋め込み参照カウントを使用するでしょう。 これは this のような生のポインタから shared_ptr を構築する方法を提供しようとする (実験的な) 試みです。 qcounted_base const * に変換可能な場合、 非メンバ関数 shared_from_this(q) は変換を実行します。

現行の実装でオプションとして採用されている侵入的カウント(intrusive counting)は、 shared_ptrintrusive_ptr (実験的な汎用の侵入的カウント型(intrusive-counted)のスマートポインタ) と相互運用することを可能にします。

もう一つのありうる実装は、 侵入的カウントのかわりにグローバルなポインタ-カウント対応マップを使用することです。 shared_from_this はもはや O(1) ではなくなることでしょう。 その操作をすることはめったにないので、 何かパフォーマンス上の問題が生じるとは私は思いませんが、 それが気にかかるユーザもいることでしょう。 グローバルなマップの保守は難しいことです。 マップは、何か shared_ptr インスタンスが構築される前に初期化されねばなりません。 そして初期化はスレッドセーフでなければなりません。 加えて、Windows のダイナミックライブラリモデルでは複数のマップが存在することがありえます。

どの実装を使用すべきか、あるいは仕様によって両者とも可能にすべきかというのは、 いまだに明確ではありません。 とは言え、経験を積んだスマートポインタのユーザからは、 this から shared_ptr を作成する能力は本質的だと考えられています。]

template<typename Y, typename D> shared_ptr(Y * p, D d);

要件: pT * に変換可能。 Dコピーコンストラクト可能(CopyConstructible) でなければならない。 D のコピーコンストラクタとデストラクタは例外を送出してはならない。 式 d(p) は整式であり、未定義な振る舞いを引きおこしたり例外を送出したりしてはならない。

結果: shared_ptr を構築。pd のコピーが格納される。

事後条件: 使用カウント が 1.

例外送出: std::bad_alloc

例外安全性: 例外が送出された場合、 d(p) が呼ばれる。

備考: p によって指し示されているオブジェクトが delete される時には、 格納されている d のコピーが p のコピーを引数として起動される。

[カスタムデアロケータは、 ファクトリ関数に shared_ptr を返させることにより、 ユーザをメモリ確保戦略から隔離することを可能にします。 デアロケータは型の一部ではないので、 メモリ確保戦略を変更しても、ソースまたはバイナリ互換性を破ることはないし、 クライアントの再コンパイルを要求することもありません。 たとえば、"no-op" デアロケータは、静的に確保されたオブジェクトへの shared_ptr を返す時に役立ちます。

カスタムデアロケータのサポートによって重大なオーバーヘッドを招くことはありません。 ???shared_ptr のその他の機能は 依然としてデアロケータが keep されることを要求します。???

D のコピーコンストラクタが例外を送出してはならないという要件は、 値渡しというところから来ています。 もしコピーコンストラクタが例外を送出すれば、ポインタはリークします。 要件を削除するには、(constな)参照渡しが要求されます。 問題は、 (1) 値渡しの場合、都合のよいことに、関数(関数の参照)を関数ポインタに変更できる (???さもなければ、これは手作業でやらなければならず、 そのようなことができないコンパイラもあります???) が、 (2) const な参照は現在のところ (標準に従うと) 関数にバインドできないということです。 この問題は、オーバーロード集合を使えば解決可能です(と思います)が、 (ISO+IEC+14882-1998 の) 14.5.5.2 問題のせいで多くのコンパイラではうまく動きません (もちろん部分順序を全くサポートしないコンパイラでも)。

この要件は、前述の問題が解決された時に削除されるでしょう。]

shared_ptr(shared_ptr const & r); // never throws
template<typename Y> shared_ptr(shared_ptr<Y> const & r); // never throws

結果: r に格納されているポインタのコピーを格納するかのようにして、 shared_ptr を構築する。

事後条件: すべてのコピーに対する 使用カウント が 1 増加する。

例外送出: なし。

[デフォルトコンストラクタで構築された shared_ptr がコピーされている時は、事後条件は緩められるでしょう。]

template<typename Y> explicit shared_ptr(weak_ptr<Y> const & r);

結果: r に格納されているポインタのコピーを格納するかのようにして、 shared_ptr を構築する。

事後条件: すべてのコピーに対する 使用カウント が 1 増加する。

Throws: use_count_is_zero when r.use_count() == 0.

Exception safety: If an exception is thrown, the constructor has no effect.

[This constructor is an optional part of the specification; it depends on the existence of weak_ptr. It is true that weak_ptr support imposes overhead on every shared_ptr user, regardless of whether weak pointers are used.

On the other hand, cyclic references are a serious problem with all reference counted designs. Not providing a solution within the library is unacceptable; if users are forced to reinvent the weak pointer wheel, there is substantial probability that they will get it wrong, as designing a safe weak_ptr interface is non-trivial.

My opinion is that the added functionality is worth the cost. weak_ptr is provided in the reference implementation as a proof of concept.]

//
explicit shared_ptr(weak_ptr const & r);
template<typename Y> explicit shared_ptr(weak_ptr<Y> const & r);

結果: shared_ptr を構築。r に格納ポインタのコピーを格納する。

事後条件: すべてのコピーに対する 使用カウント が 1 増加する。

例外送出: use_count_is_zero (r.use_count() == 0 の時)

例外安全性: 例外が送出された場合、コンストラクタは何も作用を及ぼさない。

[This constructor is an optional part of the specification; it depends on the existence of weak_ptr. It is true that weak_ptr support imposes overhead on every shared_ptr user, regardless of whether weak pointers are used.

On the other hand, cyclic references are a serious problem with all reference counted designs. Not providing a solution within the library is unacceptable; if users are forced to reinvent the weak pointer wheel, there is substantial probability that they will get it wrong, as designing a safe weak_ptr interface is non-trivial.

My opinion is that the added functionality is worth the cost. weak_ptr is provided in the reference implementation as a proof of concept.]

template<typename Y> shared_ptr(std::auto_ptr<Y> & r);

結果: shared_ptr を構築。r.release() のコピーを格納する。

//

事後条件: すべてのコピーに対する 使用カウント が 1 増加する。

Postconditions: use count is 1.

例外送出: std::bad_alloc

例外安全性: 例外が送出された場合、コンストラクタは何も作用を及ぼさない。

[This constructor takes a the source auto_ptr by reference and not by value, and cannot accept auto_ptr temporaries. This is by design, as the constructor offers the strong guarantee.]

デストラクタ

~shared_ptr(); // never throws

結果: *this が唯一の所有者 (use_count() == 1) であるならば、 格納ポインタによって指し示されるオブジェクトを破棄する。

事後条件: 残っているすべてのコピーに対する 使用カウント が 1 減少する。

例外送出: なし。

代入

shared_ptr & operator=(shared_ptr const & r); // never throws
template<typename Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws
template<typename Y> shared_ptr & operator=(std::auto_ptr<Y> & r);

結果: shared_ptr(r).swap(*this) と同値。

//

備考: 実装は、一時オブジェクトを生成することなく、異なる手段によって結果 // (および暗黙の保証) を満たすことができる。とくに、以下例で:

Notes: The use count updates caused by the temporary object construction and destruction are not considered observable side effects, and the implementation is free to meet the effects (and the implied guarantees) via different means, without creating a temporary. In particular, in the example:

shared_ptr<int> p(new int);
shared_ptr<void> q(p);
p = p;
q = p;

両方の代入で、何も行われなくてもよい。

[Some experts consider the note to be redundant, as it appears to essentially mirror the "as if" rile. However, experience suggests that when C++ code is used to describe effects, it is often misinterpreted as required implementation. In addition, it is not entirely clear whether the "as if" rule actually applies here, so it''s better to be explicit about the possible optimizations.]

初期化

void reset();

結果: shared_ptr().swap(*this) に同値。

[reset() will offer the nothrow guarantee in a future implementation.]

template<typename Y> void reset(Y * p);

結果: shared_ptr(p).swap(*this) に同値。

template<typename Y, typename D> void reset(Y * p, D d);

結果: shared_ptr(p, d).swap(*this) に同値

間接参照

T & operator*() const; // never throws

要件: 格納ポインタは 0 であってはならない。

戻り値: 格納ポインタによって指し示されるオブジェクトのリファレンス。

例外送出: なし。

T * operator->() const; // never throws

要件: 格納ポインタは 0 であってはならない。

戻り値: 格納ポインタ。

例外送出: なし。

ポインタ取得

T * get() const; // never throws

戻り値: 格納ポインタ。

例外送出: なし。

一意性

bool unique() const; // never throws

戻り値: use_count() == 1.

例外送出: なし。

//

備考: unique()use_count() よりも高速でありうる。

Notes: unique() may be faster than use_count(). If you are using unique() to implement copy on write, do not rely on a specific value when the stored pointer is zero.

[In a future release, unique() will return an unspecified value for a default-constructed shared_ptr.]

使用カウント

long use_count() const; // never throws

戻り値: 格納ポインタの所有権を共有する shared_ptr オブジェクトの数。

例外送出: なし。

備考: use_count() は必ずしも効率的ではない。 デバッグやテストにのみ用い、製品コードには使用しないこと。

変換

//
operator implementation-defined-type () const; // never throws
//
//

戻り値: 実装定義値。この値は、ブール値のコンテキストで使われたときには // get() != 0 と同値になる。

//

例外送出: なし。

//

備考: この変換オペレータにより、if (p && p->valid()) {} // のようなブール値コンテキストで shared_ptr オブジェクトを使用することが可能になる。 // 実際のターゲット型は、暗黙の型変換における数多の落とし穴を避けるため、 // メンバ関数へのポインタであることが多い。

//
operator unspecified-bool-type () const; // never throws

Returns: an unspecified value that, when used in boolean contexts, is equivalent to get() != 0.

Throws: nothing.

Notes: This conversion operator allows shared_ptr objects to be used in boolean contexts, like if (p && p->valid()) {}. The actual target type is typically a pointer to a member function, avoiding many of the implicit conversion pitfalls.

[The conversion to bool is not merely syntactic sugar. It allows shared_ptrs to be declared in conditions when using shared_dynamic_cast or make_shared.]

置換

void swap(shared_ptr & b); // never throws

結果: 2つのスマートポインタの内容を交換する。

例外送出: なし。

非メンバ関数

比較

template<typename T, typename U>
  bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

戻り値: a.get() == b.get().

例外送出: なし。

template<typename T, typename U>
  bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

戻り値: a.get() != b.get().

例外送出: なし。

template<typename T>
  bool operator<(shared_ptr<T> const & a, shared_ptr<T> const & b); // never throws
//

戻り値: operator< が、C++ 標準の 25.3節 [lib.alg.sorting] // で述べられている厳密で弱い順序付けとなるような、実装定義の値。

Returns: an unspecified value such that operator< is a strict weak ordering as described in section 25.3 [lib.alg.sorting] of the C++ standard.

例外送出: なし。

備考: shared_ptr オブジェクトが 連想コンテナのキーとして使われることを可能にする。

[Operator< has been preferred over a std::less specialization for consistency and legality reasons, as std::less is required to return the results of operator<, and many standard algorithms use operator< instead of std::less for comparisons when a predicate is not supplied. Composite objects, like std::pair, also implement their operator< in terms of their contained subobjects'' operator<.

The rest of the comparison operators are omitted by design.]

置換

template<typename T>
  void swap(shared_ptr<T> & a, shared_ptr<T> & b); // never throws

結果: a.swap(b) に同値。

例外送出: なし。

備考: std::swap のインタフェースにマッチする。 ジェネリックプログラミングに対する支援として提供されている。

[swap is defined in the same namespace as shared_ptr as this is currently the only legal way to supply a swap function that has a chance to be used by the standard library.]

get_pointer

template<typename T>
  T * get_pointer(shared_ptr<T> const & p); // never throws

Returns: p.get().

Throws: nothing.

Notes: Provided as an aid to generic programming. Used by mem_fn.

shared_static_cast

template<typename T, typename U>
  shared_ptr<T> shared_static_cast(shared_ptr<U> const & r); // never throws

要件:static_cast<T*>(r.get()) は適格 でなければならない。

戻り値: static_cast<T*>(r.get()) のコピーを格納し、 r と所有権を共有する shared_ptr<T> オブジェクト。

例外送出: なし。

備考: 一見同値に思える式

shared_ptr<T>(static_cast<T*>(r.get()))

は、同じオブジェクトを2回 delete しようとして、 結局のところ未定義の振る舞いという結果に終わるだろう。

shared_dynamic_cast

template<typename T, typename U>
  shared_ptr<T> shared_dynamic_cast(shared_ptr<U> const & r);

要件:dynamic_cast<T*>(r.get()) は適格であり、かつその振る舞いが定義されていなければならない。

戻り値:

例外送出: std::bad_alloc.

例外安全性: 例外が送出された場合、この関数は何も作用を及ぼさない。

備考: 一見同値に思える式

shared_ptr<T>(dynamic_cast<T*>(r.get()))

は、同じオブジェクトを2回 delete しようとして、 結局のところ未定義の振る舞いという結果に終わるだろう。

shared_polymorphic_cast

template<typename T, typename U>
  shared_ptr<T> shared_polymorphic_cast(shared_ptr<U> const & r);

要件: polymorphic_cast<T*>(r.get()) が適格であり、 かつ、その振る舞いが定義されていなければならない。

戻り値: polymorphic_cast<T*>(r.get()) のコピーを格納し、r と所有権を共有する shared_ptr<T> オブジェクト。

例外送出: ポインタが変換できない時、std::bad_cast

例外安全性: 例外が送出された場合、関数は何も作用を及ぼさない。

shared_polymorphic_downcast

template<typename T, typename U>
  shared_ptr<T> shared_polymorphic_downcast(shared_ptr<U> const & r); // never throws

要件: polymorphic_downcast<T*>(r.get()) が適格であり、 かつ、その振る舞いが定義されていなければならない。

戻り値: polymorphic_downcast<T*>(r.get()) のコピーを格納し、r と所有権を共有する shared_ptr<T> オブジェクト。

例外送出: なし。

完全なプログラム例については、shared_ptr_example.cpp を参照してください。 このプログラムでは、 shared_ptr オブジェクトの std::vectorstd::set を構築しています。

コンテナに値が格納された後、shared_ptr オブジェクトのいくつかは使用カウント 1 (2 以上ではなく) を保持するでしょう。 セットが std::multiset ではなく std::set であり、エントリを重複して格納しないからです。 さらには、push_backinsert といったコンテナ操作が実行されている間のさまざまなタイミングで、 使用カウントがより高くなることもあります。 もっと複雑なことには、さまざまな状況下でコンテナ操作が例外を送出するかもしれません。 スマートポインタを使わずにこの例におけるメモリ管理や例外処理を正しく扱おうとしたら、悪夢となることでしょう。

Handle/Body イディオム

shared_ptr の一般的な使用法の一つは、ヘッダーファイルに実体 (実装) をさらすことを避けるための handle/body (またの名を pimpl) イディオムを実装することです。

shared_ptr_example2_test.cpp サンプルプログラムは、 ヘッダーファイル shared_ptr_example2.hpp をインクルードします。 そこでは、実装を隠すために不完全な型への shared_ptr<> を使っています。 完全な型を必要とするメンバ関数の実体化は、 実装ファイル shared_ptr_example2.cpp で発生します。 明示的なデストラクタは不要であることに注意してください。 ~scoped_ptr とは異なり、 ~shared_ptr は T が完全な型であることを要求しません。

Thread Safety

shared_ptr objects offer the same level of thread safety as built-in types. A shared_ptr instance can be "read" (accessed using only const operations) simultaneously by multiple threads. Different shared_ptr instances can be "written to" (accessed using mutable operations such as operator= or reset) simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.)

Any other simultaneous accesses result in undefined behavior.

Examples:

shared_ptr<int> p(new int(42));

//--- Example 1 ---

// thread A
shared_ptr<int> p2(p); // reads p

// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe

//--- Example 2 ---

// thread A

p.reset(new int(1912)); // writes p

// thread B
p2.reset(); // OK, writes p2

//--- Example 3 ---

// thread A
p = p3; // reads p3, writes p

// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write

//--- Example 4 ---

// thread A
p3 = p2; // reads p2, writes p3

// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"

//--- Example 5 ---

// thread A
p3.reset(new int(1));

// thread B
p3.reset(new int(2)); // undefined, multiple writes

shared_ptr uses Boost.Config to detect whether the implementation supports threads. If your program is single-threaded, but your platform is autodetected by Boost.Config as supporting multiple threads, #define BOOST_DISABLE_THREADS to eliminate the thread safety overhead.

よくある質問

Q. 共有ポインタ (shared pointer) にはいくつのバリエーションがあって、トレードオフも異なりますね。 なぜ、スマートポインタライブラリはただ一つの実装しか提供していないのでしょう? 手近なジョブにとって最も適切なものを見つけるために、 それぞれのタイプを試してみることができると、有用だと思うのですが。
A. shared_ptr の重要な目標は、標準的な所有権共有ポインタを提供することです。 単一のポインタ型を持っていることは、安定したライブラリインタフェースのために重要です。 なぜなら、異なる共有ポインタは、通常、相互運用することができないからです。 つまり、参照カウントポインタ (ライブラリ A で使用) は、 リンクポインタ (ライブラリ B で使用) と所有権を共有することができません。

Q. なぜ shared_ptr は、ユーザによる豊富なカスタマイズを可能にするための特性 (traits) やポリシーを提供するテンプレートパラメータを持たないのでしょうか?
A. パラメータ化は、ユーザにとって妨げとなります。shared_ptr テンプレートは、 注意深く作られています、共通の要求を満たすように、広いパラメータ化なしで。 いつの日か、とても使いやすく間違いを起こしにくい、 高度な構成可能性を持ったスマートポインタが発明されるかもしれません、 その時までは shared_ptr は広範囲のアプリケーションにとって好ましいスマートポインタなのです。 (ポリシーベースのスマートポインタに興味のある人は、Andrei Alexandrescu の Modern C++ Design を読むべきです。)

Q. 私には納得できません。デフォルトパラメータは、複雑さを隠すために適切な場所で使うことができますね。 では、ポリシーはどうですか?
A. テンプレートパラメータは型に影響します。上の最初の問いに対する答えをご覧ください。

Q. Why doesn't shared_ptr use a linked list implementation?
A. A linked list implementation does not offer enough advantages to offset the added cost of an extra pointer. See timings page. In addition, it is expensive to make a linked list implementation thread safe.

Q. Why doesn't shared_ptr (or any of the other Boost smart pointers) supply an automatic conversion to T*?
A. Automatic conversion is believed to be too error prone.

Q. Why does shared_ptr supply use_count()?
A. As an aid to writing test cases and debugging displays. One of the progenitors had use_count(), and it was useful in tracking down bugs in a complex project that turned out to have cyclic-dependencies.

Q. Why doesn't shared_ptr specify complexity requirements?
A. Because complexity requirements limit implementors and complicate the specification without apparent benefit to shared_ptr users. For example, error-checking implementations might become non-conforming if they had to meet stringent complexity requirements.

Q. Why doesn't shared_ptr provide a release() function?
A. shared_ptr cannot give away 所有権 unless it's unique() because the other copy will still destroy the オブジェクト.

Consider:

shared_ptr<int> a(new int);
shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2

int * p = a.release();

// Who owns p now? b will still call delete on it in its destructor.

Q. Why doesn't shared_ptr provide (your pet feature here)?
A. Because (your pet feature here) would mandate a reference counted implementation or a linked list implementation, or some other specific implementation. This is not the intent.


Revised $Date: 2002/09/23 13:32:54 $

Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. Copyright 2002 Peter Dimov. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.