どのC ++スマートポインター実装を利用できますか?


121

比較、長所、短所、およびいつ使用するか?

これはからのスピンオフです ガベージコレクションスレッド派生したもので、私が単純な答えだと思ったものは、特定のスマートポインターの実装に関する多くのコメントを生成したため、新しい投稿を開始する価値があるように思われました。

結局のところ、問題は、C ++でのスマートポインタのさまざまな実装とは何か、そしてそれらをどのように比較するかです。単純な長所と短所、または例外と、他の方法では機能するはずだと思うかもしれないことの落とし穴

私はいくつかの実装を投稿したか、少なくとも以下の回答として使用することを検討し、100%正確ではない可能性があるそれらの違いと類似点について理解しているので、必要に応じて事実を確認または修正してください。

目標は、いくつかの新しいオブジェクトとライブラリについて学ぶか、すでに広く使用されている既存の実装の私の使用法と理解を修正して、他の人に適切なリファレンスを提供することです。


5
これは、この質問への回答として再投稿し、実際の質問にする必要があると思います。そうでなければ、人々はこれを「本当の質問ではない」と締めくくるでしょう。
ストレージャー、2011

3
ATLスマートポインターOpenSceneGraphのosg::ref_ptrような、他のあらゆる種類のスマートポインターがあります。
James McNellis、2011

11
ここに質問はありますか?
コーディグレイ

6
誤解していると思いますstd::auto_ptrstd::auto_ptr_refのデザイン詳細ですstd::auto_ptrstd::auto_ptrガベージコレクションとは何の関係もありません。主な目的は、特に関数呼び出しや関数の戻り状況において、例外の安全な所有権の譲渡を可能にすることです。std::unique_ptrC ++では移動とコピーを区別できるように変更されており、標準のコンテナはこれを利用するように変更されているため、標準のコンテナで引用する「問題」のみを解決できます。
CBベイリー

3
あなたはスマートポインターの専門家ではないと言いますが、要約はかなり網羅的で正しいです(auto_ptr_ref実装の詳細であるという軽微な問題を除いて)。それでも、これを回答として投稿し、質問を実際の質問に再構成する必要があることに同意します。これは、将来の参照として役立ちます。
Konrad Rudolph

回答:


231

C ++ 03

std::auto_ptr-おそらく、最初のドラフトシンドロームの影響を受けたオリジナルの1つだけが、限られたガベージコレクション機能しか提供していません。最初の欠点はdelete、破棄が必要になるため、配列に割り当てられたオブジェクト(new[])を保持することができないことです。ポインターの所有権を取得するため、2つの自動ポインターに同じオブジェクトを含めることはできません。割り当ては所有権を転送し、右辺値自動ポインタをnullポインタにリセットします。これはおそらく最悪の欠点につながります。前述のコピーができないため、STLコンテナ内では使用できません。ユースケースに対する最後の打撃は、C ++の次の標準で非推奨になる予定です。

std::auto_ptr_ref-これはスマートポインターではなく、std::auto_ptr特定の状況でのコピーと割り当てを可能にするために実際に使用されるデザインの詳細です。具体的には、移動コンストラクターとしても知られるColvin-Gibbonsトリックを使用して、非const std::auto_ptr左辺値に変換し、所有権を転送するために使用できます。

それどころかstd::auto_ptr、自動ガベージコレクション用の汎用スマートポインタとして使用することは、実際には意図されていなかったのでしょう。私の限られた理解と仮定のほとんどは、Herb Sutterのauto_ptrの効果的な使用に基づいており、常に最適化されているわけではありませんが、定期的に使用しています。


C ++ 11

std::unique_ptr-これは、配列std::auto_ptrを操作するstd::auto_ptrような弱点、プライベートコピーコンストラクターによる左辺値保護、STLコンテナーやアルゴリズムで使用できるなどの弱点を修正するための重要な改善を除いて、非常に似ています。メモリーフットプリントが限られているため、これは、生のポインターを置き換える、または所有者として適切に説明されている生のポインターの理想的な候補です。「一意」が意味するのは、前述のようにポインタの所有者が1人だけであることstd::auto_ptrです。

std::shared_ptr-これはTR1に基づいていると思いますboost::shared_ptrが、エイリアシングとポインタ演算も含めるように改善されました。つまり、動的に割り当てられたオブジェクトの周りに、参照カウントされたスマートポインタをラップします。「共有」とは、最後の共有ポインタの最後の参照がスコープ外になると、ポインタが複数の共有ポインタによって所有される可能性があることを意味するため、オブジェクトは適切に削除されます。これらはスレッドセーフでもあり、ほとんどの場合、不完全な型を処理できます。デフォルトのアロケーターを使用して、1つのヒープ割り当てでstd::make_shared効率的にを構築できstd::shared_ptrます。

std::weak_ptr-同様に、TR1およびboost::weak_ptr。これはが所有するオブジェクトへの参照であるstd::shared_ptrため、std::shared_ptr参照カウントがゼロになってもオブジェクトの削除は妨げられません。生のポインタにアクセスするには、最初ににアクセスする必要があります。所有するポインタが期限切れで既に破棄されている場合は、std::shared_ptrを呼び出すlockと空が返さstd::shared_ptrれます。これは主に、複数のスマートポインターを使用するときに参照カウントが無期限にぶら下がるのを防ぐのに役立ちます。


ブースト

boost::shared_ptr-おそらく、さまざまなシナリオ(STL、PIMPL、RAIIなど)で使用するのが最も簡単です。これは、共有参照カウントスマートポインターです。一部の状況ではパフォーマンスとオーバーヘッドについていくつかの不満を聞いたことがありますが、引数が何であったか思い出せないので、それらを無視したに違いありません。どうやらそれは保留中の標準C ++オブジェクトになるほど人気が​​あり、スマートポインタに関する標準を超える欠点は頭に浮かびません。

boost::weak_ptr-以前のの説明とstd::weak_ptr同様に、この実装に基づいて、これはへの非所有参照を許可しますboost::shared_ptrlock()「強い」共有ポインタにアクセスするために驚くことではないので、すでに破棄されている可能性があるため、有効であることを確認する必要があります。返された共有ポインターを保存しないようにしてください。共有ポインターを使い終わったらすぐにスコープから外してください。そうしないと、参照カウントがハングし、オブジェクトが破棄されない循環参照の問題に戻ります。

boost::scoped_ptr-これは、オーバーヘッドがほとんどない単純なスマートポインタクラスでありboost::shared_ptr、使用可能な場合よりもパフォーマンスが向上するように設計されています。これstd::auto_ptrは、STLコンテナーの要素として、または同じオブジェクトへの複数のポインターで安全に使用できないという事実に特に匹敵します。

boost::intrusive_ptr-私はこれを使用したことがありませんが、私の理解から、独自のスマートポインター互換クラスを作成するときに使用するように設計されています。参照カウントを自分で実装する必要があります。クラスをジェネリックにする場合は、いくつかのメソッドも実装する必要があります。さらに、独自のスレッドセーフを実装する必要があります。プラスの面では、これはおそらく、どれだけ多くの、またはどれだけ小さな「スマートさ」が必要かを正確に選択する最もカスタムな方法を提供します。オブジェクトごとに1つのヒープを割り当てることができるため、intrusive_ptr通常より効率的ですshared_ptr(Arvidに感謝)

boost::shared_array-これはboost::shared_ptr配列用です。基本的にnew []operator[]、もちろんdelete []で焼かれています。これは、STLコンテナで使用され、私の知る限りでは、すべてが行うことができboost:shared_ptr、使用することはできませんが、ないboost::weak_ptrこれらと。ただし、代わりboost::shared_ptr<std::vector<>>に同様の機能にを使用してboost::weak_ptr、参照に使用する機能を取り戻すこともできます。

boost::scoped_array-これはboost::scoped_ptr配列用です。boost::shared_arrayすべての必要な配列の良さと同様に、これは焼き付けられます。これはコピー不可であるため、STLコンテナーでは使用できません。私はあなたがおそらくあなたがこれを使いたいと思っているあなたがおそらくただ使うことができるであろうほとんどどこでも見つけましたstd::vector。どちらが実際に高速であるか、オーバーヘッドが少ないかは特定していませんが、このスコープ配列はSTLベクトルよりもはるかに複雑ではないようです。スタックへの割り当てを維持したい場合は、boost::array代わりに検討してください。


Qt

QPointer-Qt 4.0で導入されたこれは、「弱い」スマートポインターでありQObject、Qtフレームワークでは派生クラスでのみ機能します。ほとんどすべてであるため、実際には制限ではありません。ただし、「強力な」ポインタを提供しないという制限があります。また、基になるオブジェクトが有効であるかどうかを確認isNull()できますが、特にマルチスレッド環境では、その確認に合格した直後にオブジェクトが破棄されていることがわかります。Qtの人々はこれを非難したと考えています。

QSharedDataPointer-これは「強力な」スマートポインタboost::intrusive_ptrです。スレッドセーフが組み込まれていますが、参照カウントメソッド(refおよびderefいますが、サブクラス化によって実行できる)をですQSharedData。多くのQtと同様に、オブジェクトは十分な継承とサブクラス化を通じて最適に使用され、すべてが意図された設計のようです。

QExplicitlySharedDataPointer-をQSharedDataPointer暗黙的に呼び出さないことを除いて、非常に似ていますdetach()。私はこのバージョン2.0をQSharedDataPointer、参照カウントがゼロに落ちた後、いつデタッチするかについての制御のわずかな増加は、まったく新しいオブジェクトに特に価値がないので、ます。

QSharedPointer-原子参照カウント、スレッドセーフ、共有可能なポインター、カスタム削除(配列のサポート)は、スマートポインターのすべてのように聞こえます。これは主にQtでスマートポインターとして使用するものであり、boost:shared_ptrあり、Qtの多くのオブジェクトのようにおそらくはるかに多くのオーバーヘッドますが、

QWeakPointer-再発パターンを感じますか?同様にstd::weak_ptrboost::weak_ptrこれはQSharedPointer、オブジェクトが削除されない2つのスマートポインター間の参照が必要な場合に使用されます。

QScopedPointer-この名前も見慣れたものに見えるはずであり、実際にboost::scoped_ptrはQtバージョンの共有ポインターとウィークポインターとは異なります。それはのオーバーヘッドなしに、単一の所有者のスマートポインタを提供するために、機能QSharedPointerの互換性、例外安全なコード、そしてあなたが使用する可能性のあるすべてのもののためにそれがより適しているstd::auto_ptrboost::scoped_ptrについて。


1
私が言及する価値があると思う2つのこと:オブジェクトごとに単一のヒープ割り当てを持つことができるintrusive_ptrためshared_ptr、通常はよりも効率的です。shared_ptr一般的には、参照カウンタ用に別の小さなヒープオブジェクトを割り当てます。std::make_shared両方の世界を最大限に活用するために使用できます。shared_ptrヒープ割り当ては1つだけです。
Arvid、2011

私はおそらく無関係な質問があります:すべてのポインターをshared_ptrsで置き換えるだけでガベージコレクションを実装できますか?(循環参照の解決は含まれません)
セスカーネギー

@Seth Carnegie:すべてのポインタが無料ストアに割り当てられたものを指しているわけではありません。
インシリコ2011年

2
@the_mandrillしかし、所有するクラスのデストラクタがクライアントコードとは別の変換単位(.cpp-file)で定義されている場合に機能します。これはPimpl-idiomで指定されています。この変換ユニットは通常、Pimplの完全な定義を知っているため、そのデストラクタ(auto_ptrを破棄する場合)は正しくPimplを破棄します。これらの警告を見たときも私はこれを恐れていましたが、試してみたところ機能しました(Pimplのデストラクタが呼び出されます)。PS:返信を表示するには、@-構文を使用してください。
クリスチャン・ラウ

2
ドキュメントへの適切なリンクを追加することにより、リストの有用性が高まりました。
ulidtko 2013年

5

ポリシーベースのスマートポインターを実装するLokiもあります。

ポリシーベースのスマートポインターに関するその他のリファレンスは、多くのコンパイラーによる多重継承とともに、空の基本最適化のサポートが不十分であるという問題に対処しています。


1

与えられたものに加えて、いくつかの安全志向のものもあります:

SaferCPlusPlus

mse::TRefCountingPointerは、のような参照カウントのスマートポインタですstd::shared_ptr。違いmse::TRefCountingPointerは、安全で小さく、高速ですが、スレッドセーフティメカニズムがありません。また、「nullではない」バージョンと「修正された」(リターゲット不可)バージョンがあり、常に有効に割り当てられたオブジェクトを指していると安全に想定できます。したがって、基本的に、ターゲットオブジェクトが非同期スレッド間で共有されているstd::shared_ptr場合mse::TRefCountingPointerはを使用し、そうでない場合はより最適です。

mse::TScopeOwnerPointerはに似てboost::scoped_ptrいますがmse::TScopeFixedPointer、「」std::shared_ptrや「」のような「強弱」ポインタ関係で機能しstd::weak_ptrます。

mse::TScopeFixedPointerスタックに割り当てられているオブジェクト、または「所有」ポインタがスタックに割り当てられているオブジェクトを指します。実行時のコストをかけずにコンパイル時の安全性を高めるために、(意図的に)機能が制限されています。「スコープ」ポインターのポイントは、基本的に、(実行時の)安全メカニズムが不要であるほど十分に単純で決定論的な一連の状況を識別することです。

mse::TRegisteredPointerターゲットオブジェクトが破棄されるときに値が自動的にnull_ptrに設定されることを除いて、生のポインタのように動作します。ほとんどの状況で、生のポインタの一般的な代替として使用できます。生のポインタのように、それは本質的なスレッドの安全性を持っていません。ただし、その代わりに、スタックに割り当てられたオブジェクトをターゲットにすること(および対応するパフォーマンス上の利点を取得すること)には問題はありません。ランタイムチェックが有効になっている場合、このポインタは無効なメモリにアクセスしても安全です。mse::TRegisteredPointer有効なオブジェクトを指すときのrawポインターと同じ動作をするため、コンパイル時のディレクティブで「無効」にして(対応するrawポインターに自動的に置き換える)、デバッグ/テストでバグをキャッチするために使用できるようにすることができます/ betaモード。リリースモードではオーバーヘッドコストは発生しません。

理由と使用方法を説明した記事を次に示します。(注、恥知らずなプラグ。)

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