スマートポインター:オブジェクトの所有者は誰ですか?[閉まっている]


114

C ++はすべてメモリの所有権、つまり所有権のセマンティクスです。

動的に割り当てられたメモリのチャンクの所有者は、そのメモリを解放する必要があります。したがって、問題は実際には誰がメモリを所有するかになります。

C ++での所有権は、生のポインターが内部にラップされるタイプによって文書化されているため、優れた(IMO)C ++プログラムでは、生のポインターが渡されるのを確認することは非常にまれです(まれではありませ)(生のポインターには推論された所有権がないため、誰がメモリの所有者であるかを知らないため、ドキュメントを注意深く読まなければ、誰が所有者であるかはわかりません)

逆に、クラスに生のポインタが格納されていることはまれです。各生のポインタは、独自のスマートポインタラッパー内に格納されます。(注:オブジェクトを所有していない場合は、オブジェクトがスコープ外になり、破棄されるタイミングがわからないため、オブジェクトを格納しないでください。)

だから質問:

  • 人々が遭遇した所有権のセマンティクスのタイプは何ですか?
  • これらのセマンティクスを実装するために使用される標準クラスは何ですか?
  • どのような場合に役立ちますか?

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

概要:

概念的には、スマートポインターは単純で、単純な実装は簡単です。私は多くの試みられた実装を見てきましたが、それらは常に何らかの方法で壊れており、何気ない使用や例には明らかではありません。したがって、自分でロールするのではなく、ライブラリから十分にテストされたスマートポインタを常に使用することをお勧めします。std::auto_ptrまたは、Boostスマートポインターの1つが私のニーズをすべてカバーしているようです。

std::auto_ptr<T>

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

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

boost::scoped_ptr<T>

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

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

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

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

使用法:オブジェクトが、コンパイル時に決定できない存続期間を持つ複数の所有者を持つことができる場合。

boost::weak_ptr<T>

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

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


14
?? 質問は何でしたか?
ペーチェリエ

9
私はこの質問が投稿されましたので、auto_ptrには(今標準化さ)unique_ptrをの賛成で廃止されましたことを指摘したかった
フアンCampa

In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good (IMO) これは言い換えることができますか?全然わからない。
lolololol ol

@lololololol文章を半分に切りました。In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good C++ program it is very rare to see RAW pointers passed around。RAWポインターには所有権のセマンティクスがありません。所有者がわからない場合は、オブジェクトを削除した責任者がわかりません。所有権を定義するポインタ(std :: shared_ptr、std :: unique_ptrなど)をラップするために使用されるいくつかの標準クラスがあります。ポインターの削除を担当するユーザーを定義します。
マーティンヨーク

1
C ++ 11 +では、auto_ptrを使用しないでください。代わりにunique_ptrを使用してください!
ヴァルはモニカを復活

回答:


20

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

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

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

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

私はこれらの独自の実装を持っていますが、でも利用できますBoost

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

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


2
独自のポインタークラス(hub_ptr)を作成する代わりに、* thisをこれらのオブジェクトに渡して、参照として格納させてみませんか?オブジェクトが所有するクラスと同時に破壊されることを認めさえしているので、私はそれほど多くのフープを飛び越えることの意味を理解していません。
ミシェル

4
基本的には、物事を明確にするための設計契約です。子オブジェクトがhub_ptrを受信すると、指定されたオブジェクトは子の存続期間中に破棄されず、その所有権を持たないことがわかります。包含オブジェクトとコンテナオブジェクトの両方が、明確なルールセットに同意します。ネイキッドポインターを使用する場合、ルールを文書化できますが、コンパイラーとコードによって強制されません。
Fabio Ceconello、2009年

1
また、リリースビルドでは#ifdefsを使用してhub_ptrをネイキッドポインターにtypedefすることができるため、オーバーヘッドはデバッグビルドにのみ存在します。
Fabio Ceconello、2009年

3
Boostのドキュメントは、scoped_ptrの説明と矛盾することに注意してください。それはnoncopyableあり、所有権を譲渡することはできないと述べています。
アレックトーマス

3
@アレック・トーマス、あなたは正しい。auto_ptrについて考えていて、scoped_ptrを作成しました。修正。
Fabio Ceconello、

23

単純なC ++モデル

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

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

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

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

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

スマートポインタでいっぱいのコードでは、ユーザーはオブジェクトの有効期間を無視することを期待できます。所有者がユーザーコードになることはありません。これはスマートポインタ自体です(RAIIも同様)。問題は、参照カウントされたスマートポインターと混合された循環参照が致命的になる可能性があるため、共有ポインターとウィークポインターの両方を処理する必要があることです。したがって、考慮すべき所有権はまだあります(たとえ、生のポインターよりも有利であることを示している場合でも、弱いポインターは何も指していない可能性があります)。

  • boost :: shared_ptr
  • boost :: weak_ptr

結論

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


10

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

これにより、すべてがどのように相互作用するかを理解する必要があるため、問題の100%が解決されます。


2
  • 共有所有権
  • boost :: shared_ptr

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


2

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


2
shared_ptrが最も一般的です。しかし、もっとたくさんあります。それぞれに独自の使用パターンがあり、訴訟の良い場所と悪い場所があります。もう少し説明がいいでしょう。
マーティンヨーク

古いコンパイラーを使用している場合は、boost :: shared_ptr <blah>がstd :: tr1 :: shared_ptr <blah>のベースになっています。これは、コンパイラがBoostの最新バージョンでサポートされていない場合でも、Boostからリッピングして使用できるほど単純なクラスです。
ブラナン2008

2

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

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


1
  • 1人の所有者
  • boost :: scoped_ptr

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

簡単に取り付け直し、リークを心配することなく解放できるため、これは便利です。


1

私は自分のデザインの所有権を共有する立場になったことはないと思います。実際、頭の上から私が考えることができる唯一の有効なケースはFlyweightパターンです。


1

yasper :: ptrは軽量で、boost :: shared_ptrに似ています。私の(今のところ)小さなプロジェクトでうまく機能します。

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

別のC ++スマートポインターを作成する理由 C ++用のいくつかの高品質スマートポインター実装がすでに存在します。最も顕著なのは、Boostポインターパンテオンと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

頻繁に使用される単一の譲渡可能な所有者の別の形式があり、割り当てのセマンティクスの異常な破損auto_ptrによって引き起こされる問題を回避するため、それが望ましいauto_ptrです。

他に何も言わないswap。適切なswap関数を持つタイプは、コンテンツを入れ替えることによって所有権が同じタイプの別のインスタンスに移されるまで所有するコンテンツへのスマートな参照と考えることができます。各インスタンスはIDを保持しますが、新しいコンテンツにバインドされます。安全に再バインド可能な参照のようなものです。

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

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


多分それは必要が少なくなりますが(scoped_ptrはこれより必要が少なくなると思います)、それはなくなることはありません。ヒープに何かを割り当て、それを削除する前に誰かがスローしたり、単に忘れたりした場合、スワップ関数を使用してもまったく役に立ちません。
ミシェル

それはまさに私が最後の段落で言ったことです。
Daniel Earwicker、2009年

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.