スマートポインター(ブースト)の説明


220

次のポインターのセットの違いは何ですか?生産コードで各ポインタをいつ使用しますか?

例をいただければ幸いです!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

プロダクションコードでboostを使用していますか?

回答:


339

スマートポインターの基本プロパティ

各スマートポインターを割り当てることができるプロパティがある場合は簡単です。3つの重要なプロパティがあります。

  • 所有権はまったくありません
  • 所有権の譲渡
  • 所有権の共有

1つ目は、スマートポインタがオブジェクトを所有していないため、オブジェクトを削除できないことを意味します。2つ目は、1つのスマートポインタだけが同時に同じオブジェクトを指すことができることを意味します。関数からスマートポインターが返される場合、所有権は、たとえば返されたスマートポインターに転送されます。

3番目は、複数のスマートポインターが同じオブジェクトを同時に指すことができることを意味します。これは生のポインタにも当てはまりますが、生のポインタには重要な機能がありません。それらは所有しているかどうかを定義しません。所有権の共有スマートポインタは、すべての所有者がオブジェクトを放棄した場合、オブジェクトを削除します。この動作はたまに必要になることがあるので、共有の所有スマートポインターは広く普及しています。

一部の所有スマートポインタは、2番目も3番目もサポートしていません。したがって、関数から返すことも、他の場所に渡すこともできません。これはRAII、スマートポインタがローカルに保持され、オブジェクトがスコープ外に出た後にオブジェクトを解放するように作成された目的に最も適しています。

所有権の共有は、コピーコンストラクターを使用して実装できます。これは自然にスマートポインタをコピーし、コピーとオリジナルの両方が同じオブジェクトを参照します。あるオブジェクトから言語でサポートされている別のオブジェクトに何かを転送する手段がないため、所有権の転送は現在C ++で実際には実装できません。関数からオブジェクトを返そうとすると、オブジェクトがコピーされます。したがって、所有権の譲渡を実装するスマートポインタは、コピーコンストラクタを使用して所有権の譲渡を実装する必要があります。ただし、要件は、これらのスマートポインターのこのいわゆる「移動コンストラクター」動作と互換性のない、コンテナーの要素のコピーコンストラクターの特定の動作を記述するため、コンテナーでの使用を中断します。

C ++ 1xは、いわゆる「移動コンストラクター」および「移動割り当て演算子」を導入することにより、所有権の移転に対するネイティブサポートを提供します。また、と呼ばれるこのような所有権移転スマートポインタも付属していますunique_ptr

スマートポインターの分類

scoped_ptr転送も共有もできないスマートポインタです。ローカルでメモリを割り当てる必要がある場合にのみ使用できますが、スコープから外れたときに必ず解放されます。ただし、別のscoped_ptrと交換することもできます。

shared_ptr所有権を共有するスマートポインタです(上記の3番目の種類)。これは参照カウントされているため、最後のコピーがスコープから外れ、管理対象のオブジェクトが解放されるタイミングを確認できます。

weak_ptr所有していないスマートポインタです。参照カウントを追加せずに、(shared_ptrによって管理される)管理対象オブジェクトを参照するために使用されます。通常は、生のポインタをshared_ptrから取り出してコピーする必要があります。ただし、オブジェクトが実際にいつ削除されたかを確認する方法がないため、安全ではありません。したがって、weak_ptrは、shared_ptrが管理するオブジェクトを参照することで手段を提供します。オブジェクトにアクセスする必要がある場合は、オブジェクトの管理をロックして(別のスレッドでオブジェクトを使用しているときにshared_ptrがオブジェクトを解放しないようにする)、それを使用できます。weak_ptrがすでに削除されているオブジェクトを指している場合は、例外がスローされて通知されます。weak_ptrの使用は、循環参照がある場合に最も有益です。参照カウントは、このような状況に簡単に対処することができません。

intrusive_ptrshared_ptrに似ていますが、shared_ptrに参照カウントを保持しませんが、管理対象のオブジェクトで定義する必要があるいくつかのヘルパー関数にカウントの増減を任せます。これには、すでに参照されているオブジェクト(外部参照カウントメカニズムによって参照カウントがインクリメントされる)をintrusive_ptrに詰め込むことができるという利点があります。これは、参照カウントがスマートポインターの内部になく、スマートポインターが既存の参照カウントメカニズム。

unique_ptr所有権の譲渡ポインタです。コピーすることはできませんが、C ++ 1xのmoveコンストラクターを使用して移動できます。

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

これはstd :: auto_ptrが従うセマンティクスですが、移動のネイティブサポートがないため、落とし穴なしでそれらを提供することはできません。unique_ptrは、移動セマンティクスの重要な機能の1つである一時的な他のunique_ptrからリソースを自動的に盗みます。auto_ptrは、unique_ptrに代わり、次のC ++標準リリースで非推奨になります。C ++ 1xでは、移動のみが可能でコンテナにコピーできないオブジェクトのスタッフィングも許可されます。したがって、たとえば、unique_ptrをベクトルに詰め込むことができます。これについて詳しく知りたい場合はここで停止し、これに関する良い記事を紹介します。


3
賞賛男に感謝します。感謝しますので、あなたも今すぐ+1を取得できます:p
ヨハネスシャウブ-litb

@litb:私は「所有権の譲渡」に疑問を持っています。私は何もありません同意しない本当の C ++ 03、でなく、スマートポインタのためのオブジェクトの間で所有権の移転は、このことで、行うことができない破壊的なコピーここで述べたメカニズムinformit.com/articles/article.aspx?p=31529&seqNum= 5
legends2k 2010年

3
素晴らしい答え。注:auto_ptrはすでに非推奨です(C ++ 11)。
nickolay 2012年

2
「コンテナの要素のコピーコンストラクターの特定の動作は、これらのスマートポインターのこのいわゆる「移動コンストラクター」の動作と互換性がないと規定されているため、これにより、コンテナーでの使用が中断されます。」その部分を得られなかった。
Raja

また、キャッシュの一貫性を向上さintrusive_ptrせるshared_ptrためには、より望ましいと言われています。参照カウントを個別のオブジェクトではなく管理対象オブジェクト自体のメモリの一部として保存すると、キャッシュのパフォーマンスが向上するようです。これは、管理対象オブジェクトのテンプレートまたはスーパークラスで実装できます。
Eliot

91

scoped_ptrは最も単純です。範囲外になると破棄されます。次のコードは不正です(scoped_ptrsはコピーできません)が、ポイントを示しています。

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

shared_ptrは参照カウントされます。コピーまたは割り当てが発生するたびに、参照カウントがインクリメントされます。インスタンスのデストラクタが起動されるたびに、未加工のT *の参照カウントが減少します。0になると、ポインターは解放されます。

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

weak_ptrは共有ポインターへの弱い参照であり、ポイントされたshared_ptrがまだ存在するかどうかを確認する必要がある

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

intrusive_ptrは通常、使用する必要があるサードパーティのスマートptrがある場合に使用されます。参照カウントを追加およびデクリメントする無料の関数を呼び出します。詳細については、ドキュメントを強化するためのリンクを参照してください。


if (tPtrAccessed[0].get() == 0)と思いませんif (tPtrAccessed.get() == 0) か?
Rajeshwar 2014年

@DougT。Javaはリファレンスと同じ考え方を使用していると思いますか?ソフト、ハード、ウィークなど?
gansub 2019

20

boost::ptr_containerブーストスマートポインターの調査で見落とさないでください。たとえば、eg std::vector<boost::shared_ptr<T> >が遅すぎるような状況では、これらは非常に貴重です。


実際、前回試してみたところ、少なくとも典型的なPCハードウェアで、最初にこれを書いて以来、ベンチマークの結果、パフォーマンスのギャップが大幅に縮小したことがわかりました。より効率的なptr_containerアプローチは、ニッチなユースケースでもいくつかの利点があります。
火曜日

12

ドキュメンテーションを見ることに関するアドバイスの2番目です。見た目ほど怖くないです。そしていくつかの短いヒント:

  • scoped_ptr-範囲外になると、ポインターは自動的に削除されます。注-割り当てはできませんが、オーバーヘッドは発生しません
  • intrusive_ptr-のオーバーヘッドのない参照カウントポインターsmart_ptr。ただし、オブジェクト自体は参照カウントを格納します
  • weak_ptr -と一緒に動作します shared_ptr循環依存が発生する状況に対処します(ドキュメントを読んで、グーグルで素敵な画像を検索してください;)
  • shared_ptr -(boostによって提供されるものからの)スマートポインターの一般的で最も強力な(そしてヘビー級の)
  • またauto_ptr、コントロールがスコープを離れると、ポイントするオブジェクトが自動的に破棄されることを保証するoldもあります。ただし、他の人とはコピーのセマンティクスが異なります。
  • unique_ptr- C ++ 0xで来ます

編集への応答: はい


8
ブーストのドキュメントが怖すぎるので、ここに来ました。
Francois Botha 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.