web-dev-qa-db-ja.com

ポインター、スマートポインター、または共有ポインター?

私は通常のポインターでプログラミングしていますが、スマートポインターを実装するBoostなどのライブラリについて聞いたことがあります。また、Ogre3Dレンダリングエンジンでは、共有ポインターが深く使用されていることも確認しました。

3つの違いは正確に何ですか?それらのタイプのみを使用し続ける必要がありますか?

113
tunnuz

Sydiusは、型の概要をかなりよく説明しています。

  • 通常のポインタはまさにそれです-それらはどこかのメモリ内の何かを指します。誰が所有していますか?コメントのみがあなたに知らせます。誰がそれを解放しますか?うまくいけば、ある時点で所有者。
  • スマートポインターは、多くのタイプをカバーする包括的な用語です。 RAII パターンを使用するスコープポインターを意味すると想定します。これは、ポインターをラップするスタック割り当てオブジェクトです。範囲外になると、ラップするポインターでdeleteを呼び出します。ある時点で削除を担当するという点で、含まれるポインターを「所有」します。これらは、他のメソッドに渡すためにラップするポインターへの生の参照を取得できます。また、releasingポインターを取得して、他の誰かが所有できるようにします。それらをコピーしても意味がありません。
  • 共有ポインタはスタックを割り当てられたオブジェクトであり、ポインタをラップするため、誰が所有しているかを知る必要がありません。メモリ内のオブジェクトの最後の共有ポインターが破棄されると、ラップされたポインターも削除されます。

それらをいつ使用する必要がありますか?スコープポインターまたは共有ポインターを多用します。アプリケーションでいくつのスレッドが実行されていますか?答えが「潜在的に多く」である場合、共有ポインタはどこでも使用されるとパフォーマンスのボトルネックになることがあります。共有ポインタの作成/コピー/破棄はアトミック操作である必要があるため、多くのスレッドを実行している場合、パフォーマンスが低下する可能性があるためです。ただし、常にそうであるとは限りません-確実にわかるのはテストだけです。

共有ポインターに対して(私が気に入っている)議論があります-それらを使用することで、プログラマーがポインターの所有者を無視できるようになります。これは、循環参照(Javaはこれらを検出しますが、共有ポインターは検出できません)または大規模なコードベースでの一般的なプログラマーの怠inessを伴う厄介な状況につながる可能性があります。

スコープポインターを使用する理由は2つあります。 1つ目は、単純な例外の安全性とクリーンアップ操作です-例外が発生してもオブジェクトがクリーンアップされることを保証したい場合、およびそのオブジェクトをスタックに割り当てたくない場合は、スコープポインターに入れます。操作が成功した場合は、共有ポインターに自由に転送できますが、その間にスコープポインターを使用してオーバーヘッドを節約してください。

もう1つのケースは、オブジェクトの所有権を明確にしたい場合です。これを好むチームもあれば、好まないチームもあります。たとえば、データ構造は内部オブジェクトへのポインタを返す場合があります。スコープポインターの下では、弱い参照として扱われる生のポインターまたは参照を返します。それを所有するデータ構造が破壊された後にそのポインターにアクセスするのはエラーであり、削除するのはエラーです。共有ポインターでは、誰かがまだハンドルを保持している場合、所有オブジェクトは返された内部データを破壊できません。これにより、リソースが必要以上に長く開いたままになるか、コードによってはさらに悪化します。

143
hazzen

「スマートポインター」という用語含む共有ポインター、自動ポインター、ロックポインターなど。スマートポインターではなく、自動ポインター(より曖昧に「所有ポインター」として知られる)を言うつもりでした。

ダムポインター(T *)が最良のソリューションになることはありません。明示的なメモリ管理を行うため、冗長でエラーが発生しやすく、時には不可能になります。しかし、もっと重要なのは、彼らがあなたの意図を示していないことです。

自動ポインタは、破壊時にポインティを削除します。配列の場合、vectorやdequeなどのカプセル化を優先します。他のオブジェクトの場合、それらをヒープに格納する必要はほとんどありません。ローカルとオブジェクト構成を使用するだけです。それでも、ファクトリやポリモーフィックリターンなどのヒープポインタを返す関数では、自動ポインタが必要になります。

共有ポインタは、それへの最後の共有ポインタが破棄されると、ポインティを削除します。これは、予想されるライフタイムと所有権が状況に応じて大幅に変化する、簡単でオープンエンドのストレージスキームが必要な場合に便利です。 (アトミック)カウンターを保持する必要があるため、自動ポインターよりも少し遅くなります。共有されたポインターはシステムを設計できない人のためのものであると冗談を言って言う人もいます-自分で判断してください。

共有ポインターに不可欠な対応物については、弱いポインターも調べてください。

32
Iraimbilanja

スマートポインターは、スコープから外れると自動的にクリーンアップされます(これにより、ほとんどのメモリリークの恐れがなくなります)。共有ポインターは、ポインターのインスタンスの数をカウントし、カウントがゼロになったときにのみメモリをクリーンアップするスマートポインターです。一般に、共有ポインタのみを使用します(ただし、正しい種類を使用するようにしてください。配列には別の種類があります)。 RAII と関係があります。

21
Sydius

メモリリークを回避するために、できる限りスマートポインターを使用できます。 C++には基本的に2種類のスマートポインターがあります

  • 参照カウント(例:boost :: shared_ptr/std :: tr1:shared_ptr)
  • 非参照カウント(例:boost :: scoped_ptr/std :: auto_ptr)

主な違いは、参照カウントされたスマートポインターはコピーでき(std ::コンテナーで使用できる)、scoped_ptrはコピーできないことです。参照カウントされないポインターには、ほとんどオーバーヘッドがないか、まったくオーバーヘッドがありません。参照カウントは常に何らかのオーバーヘッドをもたらします。

(auto_ptrを避けることをお勧めします。誤って使用すると、重大な欠陥があります)

8
d0k

Sydiusの答えに少しだけ追加するために、スマートポインターは多くの場合、簡単にエラーを検出して、より安定したソリューションを提供します。生のポインタにはパフォーマンス上の利点があり、特定の状況ではより柔軟になります。また、特定のサードパーティライブラリにリンクするときに、生のポインタを使用せざるを得ない場合もあります。

5
SmacL