さて、最後に私が生計のためにC++を書いたとき、std::auto_ptr
はstd libがすべて利用可能で、boost::shared_ptr
は大流行でした。提供されている他の種類のスマートポインターを実際に調べたことはありません。 C++ 11は、boostが思いついたタイプの一部を提供するようになりましたが、すべてではありません。
誰かが、どのスマートポインターをいつ使用するかを決定する簡単なアルゴリズムを持っていますか?ダムポインター(T*
などの未加工のポインター)およびその他のブーストスマートポインターに関するアドバイスを含めることをお勧めします。 ( this のようなものは素晴らしいでしょう)。
共有所有権:
標準で採用されているshared_ptr
とweak_ptr
は、 Boostの同等物 とほとんど同じです。リソースを共有する必要があり、どれが最後に生き残るかわからない場合に使用します。 weak_ptr
を使用すると、サイクルを中断するのではなく、存続期間に影響を与えることなく共有リソースを監視できます。 shared_ptr
を使用したサイクルは通常発生しません。2つのリソースが互いに所有することはできません。
Boostには shared_array
が追加されていることに注意してください。これはshared_ptr<std::vector<T> const>
の適切な代替手段かもしれません。
次に、Boostは intrusive_ptr
を提供します。これは、リソースが既に参照カウント管理を提供していて、それをRAII原則に採用したい場合の軽量ソリューションです。これは規格に採用されていません。
一意の所有権:
Boostには scoped_ptr
もあり、これはコピーできず、削除者を指定できません。 std::unique_ptr
はステロイドではboost::scoped_ptr
であり、スマートポインターが必要な場合のデフォルトの選択である必要があります。テンプレート引数で削除機能を指定でき、boost::scoped_ptr
とは異なり、movableです。また、コピー可能な型を必要とする操作を使用しない限り、STLコンテナーで完全に使用できます(明らかに)。
Boostの配列バージョンは scoped_array
であることに注意してください。これは、delete
ing(std::unique_ptr<T[]>
rを使用)の代わりにdelete[]
ポインターをdefault_delete
部分的に特殊化することを要求することで統一された標準です。 std::unique_ptr<T[]>
は、operator[]
およびoperator*
の代わりにoperator->
も提供します。
std::auto_ptr
はまだ標準になっていますが、廃止予定です。 §D.10 [depr.auto.ptr]
クラステンプレート
auto_ptr
は非推奨です。 [注:クラステンプレートunique_ptr
(20.7.1)は、より良いソリューションを提供します。 —メモの終了]
所有権なし:
ダムポインター(生のポインター)または非所有参照リソースへの参照を使用し、リソースが存続する参照オブジェクト/スコープを知っている場合。ヌル可能またはリセット可能のいずれかが必要な場合は、参照を優先し、生のポインターを使用します。
リソースへの非所有参照が必要であるが、そのリソースがそれを参照するオブジェクトよりも存続するかどうかわからない場合は、リソースをshared_ptr
にパックし、weak_ptr
を使用します-親shared_ptr
が生きているかどうかをテストできますlock
。リソースがまだ存在する場合、null以外のshared_ptr
を返します。リソースが停止しているかどうかをテストする場合は、expired
を使用します。この2つは似ているように聞こえますが、expired
はその単一ステートメントの戻り値のみを保証するため、同時実行の面では非常に異なります。一見無害なテストのような
if(!wptr.expired())
something_assuming_the_resource_is_still_alive();
潜在的な競合状態です。
どのスマートポインターを使用するかを決定することは、所有権の問題です。リソース管理に関しては、オブジェクトA ownsオブジェクトBがオブジェクトBの存続期間を制御している場合、たとえば、メンバー変数の存続期間は関連付けられているため、メンバー変数はそれぞれのオブジェクトによって所有されます。オブジェクトの寿命まで。オブジェクトの所有方法に基づいて、スマートポインターを選択します。
ソフトウェアシステムの所有権は、ソフトウェアの外部で考えると所有権とは別のものであることに注意してください。たとえば、人は自分の家を「所有」するかもしれませんが、それは必ずしもPerson
オブジェクトがHouse
オブジェクトの存続期間を制御することを意味するわけではありません。これらの現実世界の概念をソフトウェアの概念と融合させることは、自分自身を穴にプログラムする確実な方法です。
オブジェクトの唯一の所有権がある場合は、std::unique_ptr<T>
を使用します。
オブジェクトの所有権を共有している場合...
-所有権にサイクルがない場合は、std::shared_ptr<T>
を使用します。
-サイクルがある場合、「方向」を定義し、一方の方向でstd::shared_ptr<T>
を使用し、もう一方の方向でstd::weak_ptr<T>
を使用します。
オブジェクトがあなたを所有しているが、所有者がいない可能性がある場合は、通常のポインターT*
を使用します(例:親ポインター)。
オブジェクトがあなたを所有している場合(または存在が保証されている場合)、参照T&
を使用します。
警告:スマートポインターのコストに注意してください。メモリまたはパフォーマンスが制限された環境では、メモリを管理するためのより手動のスキームで通常のポインタを使用するだけで有益な場合があります。
費用:
std::shared_ptr
には、コピー時の参照カウント増分のオーバーヘッドに加えて、破棄時のデクリメントとそれに続く保持オブジェクトの削除を伴う0カウントチェックのオーバーヘッドがあります。実装によっては、コードが肥大化し、パフォーマンスの問題が発生する可能性があります。例:
struct BinaryTree
{
Tree* m_parent;
std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};
バイナリツリーは親を所有しませんが、ツリーの存在は親(またはルートの場合はnullptr
)の存在を意味するため、通常のポインターを使用します。 (値のセマンティクスを持つ)バイナリツリーは、その子の唯一の所有権を持っているので、それらはstd::unique_ptr
です。
struct ListNode
{
std::shared_ptr<ListNode> m_next;
std::weak_ptr<ListNode> m_prev;
};
ここでは、リストノードは次のリストと前のリストを所有しているため、方向を定義し、nextにshared_ptr
を、prevにweak_ptr
を使用してサイクルを中断します。
参照カウントが必要な場合を除き、常にunique_ptr<T>
を使用します。その場合、shared_ptr<T>
を使用します(非常にまれなケースでは、参照サイクルを防ぐためにweak_ptr<T>
を使用します)。ほとんどの場合、譲渡可能な一意の所有権は問題ありません。
生のポインター:共変の戻り値が必要な場合にのみ有効です。それ以外の場合、それらは非常に便利ではありません。
配列ポインター:unique_ptr
には、結果に対してT[]
を自動的に呼び出すdelete[]
の特殊化があるため、たとえばunique_ptr<int[]> p(new int[42]);
を安全に実行できます。 shared_ptr
カスタム削除機能はまだ必要ですが、特殊な共有または一意の配列ポインターは必要ありません。もちろん、そのようなものは通常、とにかくstd::vector
に置き換えるのが最適です。残念ながらshared_ptr
は配列アクセス関数を提供しないため、get()
を手動で呼び出す必要がありますが、unique_ptr<T[]>
はoperator[]
とoperator*
ではなくoperator->
を提供します。いずれにせよ、自分で境界チェックする必要があります。これにより、shared_ptr
のユーザーフレンドリーがやや低下しますが、おそらく一般的な利点があり、Boost依存関係がないため、unique_ptr
とshared_ptr
が再び勝者になります。
スコープポインター:unique_ptr
と同様に、auto_ptr
によって無関係になりました。
本当にそれ以上何もありません。移動セマンティクスのないC++ 03では、この状況は非常に複雑でしたが、C++ 11ではアドバイスは非常に簡単です。
intrusive_ptr
やinterprocess_ptr
など、他のスマートポインターの使用法はまだあります。ただし、それらはvery nicheであり、一般的なケースでは完全に不要です。
unique_ptr
を使用する場合のケース:
shared_ptr
を使用する場合のケース:
weak_ptr
を使用する場合のケース:
自由に編集して追加してください