1. ホーム
  2. c++

[解決済み] C++のスマートポインタの実装にはどのようなものがありますか?

2022-08-11 14:49:50

質問

比較、長所、短所、使用するタイミングは?

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

最終的には、C++ におけるスマート ポインターのさまざまな実装は何であり、それらをどのように比較するのか、という質問です。 単純な長所と短所、または、そうでなければ動作するはずだと思うかもしれない何かに対する例外とゴッチャだけです。

私が使ったことのある、あるいは少なくともざっと見て、答えとして使うことを考えたいくつかの実装と、それらの違いや類似点についての私の理解を以下に掲載しました。

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

どのように解決するのですか?

C++03

std::auto_ptr - おそらくオリジナルのひとつで、限られたガベージコレクション機能しか提供しない初稿症候群に悩まされていました。最初の欠点は delete を呼び出すので、配列に割り当てられたオブジェクトを保持するためには受け入れがたいことです ( new[] ). これはポインタの所有権を取るので、2つの自動ポインタが同じオブジェクトを含むことはできません。アサインメントによって所有権が移され rvalue をヌルポインタにリセットします。これはおそらく最悪の欠点につながります。前述のようにコピーできないため、STL コンテナ内では使用できません。どのような使用例に対しても最終的な打撃は、C++ の次の標準では非推奨となる予定であることです。

std::auto_ptr_ref - これはスマートポインタではありません。 std::auto_ptr と併用され、特定の状況下でのコピーや割り当てを可能にするためのものです。具体的には、コンストラクタでない std::auto_ptr lvalue として知られている Colvin-Gibbons のトリックを使用しています。 移動コンストラクタ を使用して所有権を移譲します。

逆に、おそらく std::auto_ptr は、自動ガベージ コレクションのための汎用スマート ポインターとして使用されることを意図していなかったのでしょう。私の限られた理解や推測の大部分は Herb Sutter の auto_ptr の効果的な使用法 に基づいており、常に最も最適化された方法ではないものの、私は定期的にそれを使用しています。


C++11

std::unique_ptr - こちらは入れ替わりで登場する std::auto_ptr の弱点を修正するための重要な改善点を除けば、非常によく似たものになるでしょう。 std::auto_ptr の弱点を修正するための重要な改良が施されています。 lvalue の保護、STL コンテナやアルゴリズムでの使用などです。パフォーマンス オーバーヘッドとメモリ フットプリントが制限されているため、これは生のポインタを置き換える、あるいは所有すると表現するのがより適切な理想的な候補と言えます。unique" が意味するように、ポインタの所有者は一人であり、以前の std::auto_ptr .

std::shared_ptr - これはTR1がベースになっていると思いますが boost::shared_ptr をベースにしていますが、エイリアスやポインタ演算を含むように改良されています。要するに、参照カウントされたスマートポインタを、動的に割り当てられたオブジェクトに巻き付けるのです。shared" が意味するように、ポインタは複数の共有ポインタによって所有することができ、最後の共有ポインタの最後の参照がスコープ外になったとき、そのオブジェクトは適切に削除されるでしょう。これらはスレッドセーフでもあり、ほとんどの場合、不完全な型も扱うことができます。 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_ptr . 驚くなかれ、あなたは lock() を呼び出して共有ポインタにアクセスし、それがすでに破棄されている可能性があるため、それが有効であることを確認する必要があります。返された共有ポインタを保存せず、それを使用し終わったらすぐにスコープの外に出すようにします。そうしないと、参照カウントがハングアップしてオブジェクトが破棄されないという循環参照問題にすぐに戻ってしまいます。

boost::scoped_ptr - これはシンプルなスマートポインタ・クラスで、オーバーヘッドはほとんどありません。 boost::shared_ptr に代わるものとして設計されたものです。これは std::auto_ptr と同等ですが、STLコンテナの要素として、あるいは同じオブジェクトへの複数のポインタとして安全に使用することができないという点で特に注意が必要です。

boost::intrusive_ptr - 私はこれを使ったことがありませんが、私の理解では、これは独自のスマートポインタ互換のクラスを作成するときに使用するように設計されています。参照カウントを自分で実装する必要があり、クラスを汎用的にしたい場合はいくつかのメソッドを実装する必要があり、さらに独自のスレッドセーフを実装する必要があります。さらに、独自のスレッドセーフを実装する必要があります。プラス面では、これはおそらく、あなたが望むスマートさの程度を正確に選択する最もカスタムな方法を提供します。 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 vectorよりもはるかに少ないようです。スタック上にアロケーションを維持したい場合は boost::array を考えてみてください。


Qt

QPointer - Qt 4.0 で導入されたこのスマートポインタは、quot;weak" でしか動作しません。 QObject と派生クラスでのみ動作し、Qt フレームワークでは ほとんど であり、それは実際に制限ではありません。しかし、制限があります。すなわち、それは "strong" ポインタを提供しないことと、基礎となるオブジェクトが有効であるかどうかでチェックできるものの isNull() でチェックできますが、特にマルチスレッド環境では、そのチェックをパスした直後にオブジェクトが破壊されてしまう可能性があります。Qtの人たちはこれを非推奨と考えているようです。

QSharedDataPointer - これは、次のようなスマート・ポインタです。 boost::intrusive_ptr に匹敵するものです。スレッドセーフですが、参照カウントメソッド ( refderef ) をサブクラス化することで実現できます。 QSharedData . Qtの多くと同様に、オブジェクトは十分な継承によって使用するのが最善であり、すべてをサブクラス化することが意図された設計であるように思われます。

QExplicitlySharedDataPointer - と非常によく似ています。 QSharedDataPointer を暗黙のうちに呼び出さないことを除けば、非常によく似ています。 detach() . 私はこれをバージョン 2.0 の QSharedDataPointer 参照カウントがゼロになった後に切り離すタイミングを正確に制御するためのわずかな増加は、まったく新しいオブジェクトの価値が特にないため、これをバージョン 2.0 と呼ぶことにします。

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

QWeakPointer - 繰り返されるパターンを感じますか?ちょうど std::weak_ptrboost::weak_ptr と組み合わせて使用されます。 QSharedPointer と併用します。そうしないとオブジェクトが削除されないような スマートポインタ間の参照が必要な場合です。

QScopedPointer - この名前も見覚えがあるはずで、実はこの名前は boost::scoped_ptr Qtバージョンの共有ポインタや弱ポインタとは異なります。のオーバーヘッドなしで単一オーナーのスマートポインタを提供するために機能します。 QSharedPointer のようなオーバーヘッドがなく、互換性、例外セーフコード、および std::auto_ptr または boost::scoped_ptr のためのものです。