いつどのようなポインターを使用しますか?


228

さて、私が生計を立てるためにC ++を最後に書いたのstd::auto_ptrは、すべてのstd libが利用でき、大流行していましたboost::shared_ptr。提供されている他のスマートポインターの種類のブーストを実際に調べたことはありません。C ++ 11が、ブーストで生まれた型のいくつかを提供することを理解していますが、すべてではありません。

では、誰かがどのスマートポインターをいつ使用するかを決定する簡単なアルゴリズムを持っているのでしょうか。できれば、ダムポインター(のような生のポインターT*)とブーストスマートポインターの残りの部分に関するアドバイスを含めます。(このような何かが素晴らしいでしょう)。



1
私は、誰かがこのSTL選択フローチャートのような便利で便利なフローチャートを思い付くことを本当に望んでいます。
Alok Save

1
@アルス:ああ、それは確かにいいものです!私はそれをFAQ化しました。
sbi 2012年

6
@Deduplicatorそれは複製になることにはほど遠いです。リンクされた質問は「スマートポインターをいつ使用する必要があるか」を示し、この質問は「これらのスマートポインターをいつ使用するか」です。つまり、これは標準のスマートポインターのさまざまな使用法を分類しています。リンクされた質問はこれを行いません。違いは一見小さなものですが、大きな違いです。
Rapptz

回答:


183

共有所有権:及び標準採用し、かなり自分と同じブースト対応。リソースを共有する必要があり、どれが最後になるかわからない場合に使用します。サイクルを壊すのではなく、その寿命に影響を与えずに共有リソースを監視するために使用します。を伴うサイクルは通常発生しません。2つのリソースが互いに所有することはできません。
shared_ptrweak_ptrweak_ptrshared_ptr

注ブーストに加えて提供していることshared_arrayへの適切な代替であるかもしれません、shared_ptr<std::vector<T> const>

次に、Boostはを提供しますintrusive_ptr。これは、リソースが参照カウント管理をすでに提供していて、それをRAIIの原則に採用したい場合に、軽量のソリューションです。これは標準では採用されていません。

一意の所有権:
Boostにはもありscoped_ptr、これはコピーできず、削除者を指定できません。std::unique_ptrboost::scoped_ptrステロイドに関するもので、スマートポインタが必要な場合のデフォルトの選択です。テンプレートの引数で削除を指定でき、移動可能boost::scoped_ptrです。(明らかに)コピー可能な型を必要とする操作を使用しない限り、STLコンテナーでも完全に使用できます。

繰り返しますが、Boostには配列バージョン:scoped_arrayがあります。これは、標準で統一されています(r を使用するのではなく、ポインターを使用するのではなく、ポインターをstd::unique_ptr<T[]>部分的に特殊化する必要がdelete[]あります)。およびの代わりにも提供しています。deletedefault_deletestd::unique_ptr<T[]>operator[]operator*operator->

std::auto_ptr標準のままですが、それがされる非推奨§D.10 [depr.auto.ptr]

クラステンプレートauto_ptrは非推奨です。[ 注:クラステンプレートunique_ptr(20.7.1)は、より優れたソリューションを提供します。—エンドノート ]

所有権なし:リソースへの非所有参照には
ダムポインター(生のポインター)または参照を使用し、リソースが参照オブジェクト/スコープよりも長く存続することがわかっている場合。null可能性またはリセット可能性が必要な場合は、参照を優先し、生のポインターを使用します。

あなたは、リソースへの非所有する参照をしたいが、あなたは、リソースのオブジェクトよりも長生きするかどうかの言及は、それが、中にリソースを詰めることを知らない場合shared_ptrと使用weak_ptr-親がどうかをテストすることができますshared_ptrして生きてlockいるだろう、shared_ptrリソースがまだ存在する場合は、nullでないa を返します。リソースが停止しているかどうかをテストする場合は、を使用しますexpired。2つは同じように聞こえるかもしれませんが、同時実行に直面すると非常に異なりexpiredます。これは、その1つのステートメントの戻り値しか保証されないためです。一見無害なテストのような

if(!wptr.expired())
  something_assuming_the_resource_is_still_alive();

潜在的な競合状態です。


1
あなたは何も所有必要はありませんしない限り、無所有の場合は、おそらくポインタへの参照を好むすべきの言及も、あなたがする元のオブジェクトを書き換え検討する必要があります、それをカットされませんresettability shared_ptrとなるように非所有ポインタをa weak_ptr...
DavidRodríguez-dribeas

2
ポインタへの参照を意味するのではなく、ポインタの代わりに参照をしました。所有権がない場合は、リセット可能性(またはnull可能性は必要ですが、リセットできないnull可能性はかなり制限されます)が必要な場合を除き、最初にポインターではなくプレーンリファレンスを使用できます。
デビッドロドリゲス-12

1
@David:ああ、なるほど。:)ええ、参照はそのために悪くありません、私は個人的にそのような場合にもそれらを好みます。追加します。
Xeo

1
@Xeo:shared_array<T>shared_ptr<T[]>ないことの代替手段shared_ptr<vector<T>>です。成長することはできません。
R.マルティーニョフェルナンデス

1
@GregroyCurrie:それは...まさに私が書いたものですか?これは潜在的な競合状態の例だと言いました。
Xeo

127

使用するスマートポインターを決定することは、所有権の問題です。リソース管理に関しては、オブジェクトBの存続期間を制御している場合、オブジェクトA オブジェクトBを所有します。たとえば、メンバー変数の存続期間はオブジェクトの存続期間に関連付けられているため、メンバー変数はそれぞれのオブジェクトによって所有されます。オブジェクトの所有方法に基づいてスマートポインタを選択します。

ソフトウェアシステムの所有権は、ソフトウェアの外部で考えるのとは異なり、所有権とは別のものであることに注意してください。たとえば、人が自分の家を「所有」しているPerson場合でも、オブジェクトがオブジェクトの寿命を制御できるとは限りませんHouse。これらの現実世界の概念をソフトウェアの概念と融合させることは、自分を穴にプログラムする確実な方法です。


オブジェクトの唯一の所有権がある場合は、を使用しますstd::unique_ptr<T>

オブジェクトの所有権を共有している場合...-所有権に
循環がない場合は、を使用しますstd::shared_ptr<T>
-サイクルがある場合は、「方向」を定義し、std::shared_ptr<T>一方の方向と他方の方向で使用しstd::weak_ptr<T>ます。

オブジェクトがあなたを所有しているが、所有者がいない可能性がある場合は、通常のポインターT*(親ポインターなど)を使用します。

オブジェクトがあなたを所有している(または存在が保証されている)場合は、referenceを使用します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;
};

ここでは、リストノードが次のリストと前のリストを所有しているため、方向を定義し、shared_ptr次へweak_ptrと前への方向を使用してサイクルを壊します。


3
二分木の例では、何人かの人々はshared_ptr<BinaryTree>子供たちとweak_ptr<BinaryTree>親関係のために使うことを提案するでしょう。
デビッドロドリゲス-12

@DavidRodríguez-dribeas:ツリーに値のセマンティクスがあるかどうかによって異なります。ソースツリーが破棄された後でも、外部からツリーを参照する場合は、はい、共有/弱いポインタの組み合わせが最適です。
Peter Alexander

オブジェクトがあなたを所有し、存在することが保証されているなら、なぜリファレンスではないのでしょうか。
マーティンヨーク

1
参照を使用する場合、親を変更することはできません。これにより、デザインが妨げられる場合とされない場合があります。木のバランスをとるために、それは妨げます。
Mooing Duck、2012

3
+1しますが、最初の行に「所有権」の定義を追加する必要があります。私はしばしば、ドメイン固有の意味での所有権ではなく、オブジェクトの生死に関するものであることを明確に述べなければならないことに気づきます。
Klaim

19

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間違いなく、一般的な優位性と無ブースト依存関係になりますが、わずかに少ないユーザーフレンドリーにunique_ptrし、shared_ptr再び勝者。

スコープ付きポインタ:unique_ptrと同様に、によって無関係になりましたauto_ptr

それ以上は何もありません。移動セマンティクスのないC ++ 03ではこの状況は非常に複雑でしたが、C ++ 11ではアドバイスは非常に単純です。

intrusive_ptrまたはなど、他のスマートポインタの使用方法はまだありますinterprocess_ptr。ただし、これらは非常にニッチであり、一般的なケースでは完全に不要です。


また、反復のための生のポインタ。また、出力パラメーターバッファーの場合、バッファーは呼び出し元によって所有されます。
Ben Voigt

うーん、私がそれを読んだ方法は、共変リターンと非所有の両方の状況です。交差ではなく共用体を意味する場合は、書き換えが適切な場合があります。また、反復についても特筆に値します。
Ben Voigt

2
std::unique_ptr<T[]>およびのoperator[]代わりにoperator*を提供しますoperator->。ただし、自分で境界チェックを行う必要があることは事実です。
Xeo

8

使用するケースunique_ptr

  • ファクトリーメソッド
  • ポインターであるメンバー(pimplを含む)
  • stlコンテナーにポインターを格納する(移動を回避するため)
  • 大きなローカルダイナミックオブジェクトの使用

使用するケースshared_ptr

  • スレッド間でオブジェクトを共有する
  • 一般的なオブジェクトの共有

使用するケースweak_ptr

  • 一般的な参照として機能する大きなマップ(すべての開いているソケットのマップなど)

自由に編集して追加してください


シナリオを説明するので、私は実際にあなたの答えがより好きです。
ニコラスハンフリー、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.