web-dev-qa-db-ja.com

スマートポインター:または、赤ちゃんの所有者は誰ですか?

C++はすべてメモリの所有権に関するものです
別名 "所有権セマンティクス"

そのメモリを解放するのは、動的に割り当てられたメモリのチャンクの所有者の責任です。したがって、質問は本当に誰が記憶を所有するかになる。

C++では、RAWポインターが内側にラップされている型によって文書化されているため、良好な(IMO)C++プログラムでは、渡されるRAWポインターを見るのは非常にまれです[RARE not NEVER](RAWポインターには所有権がないため、メモリの所有者を教えてください。したがって、ドキュメントを注意深く読むことなく、所有者の責任者を知ることはできません。

逆に、クラスに格納されたRAWポインターが表示されることはまれです。各RAWポインターは、独自のSMARTポインターラッパー内に格納されます。N.B .: オブジェクトを所有していない場合は、オブジェクトがスコープ外になって破棄される時期がわからないため、オブジェクトを保存しないでください。

質問:

  • どのようなタイプの所有権セマンティックに出会いましたか?
  • これらのセマンティクスの実装に使用される標準クラスは何ですか?
  • どのような状況で役立つと思いますか?

回答ごとに1種類のセマンティック所有権を保持して、個別に上下に投票できるようにします。

概要:

概念的にはスマートポインターはシンプルで、素朴な実装は簡単です。私は多くの実装の試みを見てきましたが、それらは普段の使用や例では明らかではない何らかの形で壊れています。したがって、独自にローリングするのではなく、ライブラリから十分にテストされた「スマートポインター」を常に使用することをお勧めします。 std :: auto_ptrまたはboostスマートポインターのいずれかが、私のニーズをすべてカバーしているようです。

std :: auto_ptr <T>:

一人がオブジェクトを所有しています。
ただし、所有権の譲渡は許可されています。

使用法:
======
これにより、所有権の明示的な譲渡を示すインターフェースを定義できます。

boost :: scoped_ptr <T>

一人がオブジェクトを所有しています。
所有権の譲渡は許可されていません。

使用法:
======
明示的な所有権を示すために使用されます。
オブジェクトはデストラクタによって、または明示的にリセットされると破棄されます。

boost :: shared_ptr <T>(std :: tr1 :: shared_ptr <T>)

複数の所有権。
これは単純な参照カウントポインターです。参照カウントがゼロに達すると、オブジェクトは破棄されます。

使用法:
======
オブジェクトが、コンパイル時に決定できないライフタイムを持つ複数の花を持つことができる場合。

boost :: weak_ptr <T>

Shared_ptr <T>で使用されます。
ポインターのサイクルが発生する可能性がある状況。

使用法:
======
サイクルのみが共有参照カウントを維持しているときに、サイクルがオブジェクトを保持しないようにするために使用されます。

113
Martin York

私にとって、これらの3種類は私のニーズのほとんどをカバーしています。

shared_ptr-参照カウント、カウンターがゼロに達したときの割り当て解除

weak_ptr-上記と同じですが、shared_ptrの「スレーブ」であり、割り当て解除できません

auto_ptr-同じ関数内で作成と割り当て解除が発生する場合、またはオブジェクトを常に1人の所有者のみと見なす必要がある場合。あるポインターを別のポインターに割り当てると、2番目のポインターが最初のポインターをオブジェクトから「スチール」します。

これらには独自の実装がありますが、Boostでも利用可能です。

参照(const可能な場合はいつでも)によってオブジェクトを渡します。この場合、呼び出されたメソッドは、呼び出し時のみオブジェクトが生きていると仮定する必要があります。

hub_ptrと呼ぶ別の種類のポインターがあります。 (通常は仮想基本クラスとして)ネストされたオブジェクトからアクセスできる必要があるオブジェクトがある場合です。これは、weak_ptrをそれらに渡すことで解決できますが、shared_ptrをそれ自体に持っていません。これらのオブジェクトは彼よりも長くは存続しないことがわかっているため、hub_ptrをそれらに渡します(通常のポインターへの単なるテンプレートラッパーです)。

20
Fabio Ceconello

シンプルなC++モデル

私が見たほとんどのモジュールでは、デフォルトで、ポインターの受信はnot所有権の受信であると想定されていました。実際、ポインタの所有権を放棄する関数/メソッドは非常にまれであり、ドキュメントでその事実を明示的に表しています。

このモデルは、ユーザーが明示的に割り当てるもののみの所有者であると想定しています。その他はすべて自動的に破棄されます(スコープの出口で、またはRAIIを介して)。これはCに似たモデルであり、ほとんどのポインターは自動的にまたは必要に応じて(ほとんどの場合オブジェクトの破壊時に)割り当てを解除するオブジェクトによって所有され、オブジェクトの存続期間は予測可能です(RAIIはあなたの友人です)再び)。

このモデルでは、生のポインターは自由に循環しており、ほとんど危険ではありません(ただし、開発者が十分に賢い場合は、可能であれば代わりに参照を使用します)。

  • 生のポインタ
  • std :: auto_ptr
  • boost :: scoped_ptr

スマートポインテッドC++モデル

スマートポインターで満たされたコードでは、ユーザーはオブジェクトの有効期間を無視することができます。所有者は決してユーザーコードではありません。それはスマートポインターそのものです(再びRAII)。 問題は、参照カウントのスマートポインターと混合した循環参照が致命的になる可能性があることですです。そのため、共有ポインターと弱いポインターの両方を処理する必要があります。ですから、あなたはまだ検討すべき所有権を持っています(生のポインタよりもその利点があなたにそう言うことができるとしても、弱いポインタは何も指し示すことはできません)。

  • boost :: shared_ptr
  • boost :: weak_ptr

結論

私が説明するモデルに関係なく、例外を除いて、ポインターを受け取ることはnotその所有権を受け取ることおよびそれは誰が誰を所有しているかを知ることは依然として非常に重要です。参照やスマートポインターを多用するC++コードでも。

23
paercebal

所有権を共有しないでください。その場合は、自分で制御できないコードのみを使用してください。

すべてがどのように相互作用するかを理解することを強制するので、それは問題の100%を解決します。

10
MSN
  • 共有所有権
  • boost :: shared_ptr

リソースが複数のオブジェクト間で共有されている場合。 boost shared_ptrは参照カウントを使用して、すべての人が完了したときにリソースが確実に割り当て解除されるようにします。

2
Martin York

std::tr1::shared_ptr<Blah>は多くの場合、最善の策です。

2
Matt Cruikshank

Boostからは、 ポインタコンテナ ライブラリもあります。これらは、コンテナのコンテキストでのみオブジェクトを使用する場合、スマートポインタの標準コンテナよりも少し効率的で使いやすいです。

Windowsには、COMポインター(IUnknown、IDispatch、およびフレンド)と、それらを処理するためのさまざまなスマートポインター(たとえば、ATLの CComPtr および「import」ステートメントによって自動生成されるスマートポインターがあります。 _ com_ptr クラスに基づくVisual Studio)。

2
Ryan Ginstrom
  • ワンオーナー
  • boost :: scoped_ptr

メモリを動的に割り当てる必要があるが、ブロックのすべての出口点で確実に割り当てを解除する必要がある場合。

リークを心配することなく簡単に取り付けられ、解放できるので、これが便利だと思います

1
Pieter

私は自分のデザインの所有権を共有する立場にあったとは思わない。実際、頭の上から考えられる唯一の有効なケースは、フライウェイトパターンです。

1

yasper :: ptrは軽量で、boost :: shared_ptrのような代替手段です。私の(今のところ)小さなプロジェクトでうまく機能します。

http://yasper.sourceforge.net/ のWebページでは、次のように記述されています。

別のC++スマートポインターを記述する理由C++用のいくつかの高品質スマートポインター実装が既に存在します。最も顕著なのは、ブーストポインターパンテオンとLokiのSmartPtrです。スマートポインターの実装を適切に比較し、その使用が適切な場合は、Herb SutterのThe New C++:Smart(er)Pointersをお読みください。他のライブラリの拡張機能とは対照的に、Yasperは、狭い範囲に焦点を当てた参照カウントポインターです。 Boostのshared_ptrおよびLokiのRefCounted/AllowConversionポリシーと密接に対応しています。 Yasperを使用すると、C++プログラマは、Boostの大きな依存関係を導入したり、Lokiの複雑なポリシーテンプレートを学習したりすることなく、メモリ管理を忘れることができます。哲学

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)

Yasperは、他の実装では禁止されている危険な(まだ有用な)アクション(生のポインターへの割り当てや手動リリースなど)を許可するため、最後の点は危険です。気をつけてください、あなたがしていることを知っている場合にのみそれらの機能を使用してください!

1
Hernán

別の頻繁に使用されるsingle-transferable-ownerの形式があり、auto_ptrの割り当てセマンティクスの非常識な破損によって引き起こされる問題を回避するため、auto_ptrよりも望ましいです。

私はswap以外の何ものでもありません。適切なswap関数を持つ任意のタイプは、所有権が同じタイプの別のインスタンスに転送されるまで所有するコンテンツへのスマートリファレンスとして考えられます。それらを交換します。各インスタンスはそのIDを保持しますが、新しいコンテンツにバインドされます。これは、安全に再バインド可能な参照のようなものです。

(コンテンツを取得するために明示的に逆参照する必要がないため、スマートポインターではなくスマートリファレンスです。)

これは、auto_ptrの必要性が低くなることを意味します。型に適切なswap関数がないギャップを埋めるのに必要なだけです。しかし、すべてのstdコンテナはそうです。

1
  • 所有者1人:コピーでの別名削除
  • std :: auto_ptr

オブジェクトの作成者が所有権を他の誰かに明示的に渡したい場合。これはまた、私があなたにこれを与えているコードを文書化する方法であり、私はもはやそれを追跡していませんので、終了したら必ず削除してください。

0
Martin York