web-dev-qa-db-ja.com

shared_ptrまたはunique_ptrを使用する必要がありますか

Pimplイディオムを使用していくつかのオブジェクトを作成してきましたが、 std::shared_ptrstd::unique_ptr のどちらを使用するかわかりません。

std::unique_ptrの方が効率的であると理解していますが、これらのオブジェクトはとにかく重いため、std::shared_ptrに対するstd::unique_ptrのコストは比較的小さいため、これは私にとってそれほど問題ではありません。

柔軟性を高めるため、現在std::shared_ptrを使用しています。たとえば、std::shared_ptrを使用すると、これらのオブジェクトをハッシュマップに格納して迅速にアクセスできる一方で、これらのオブジェクトのコピーを呼び出し元に返すことができます(反復子や参照がすぐに無効になる可能性があるためです)。

ただし、変更はすべてのコピーに影響するため、これらのオブジェクトは実際にはコピーされません。したがって、おそらくstd::shared_ptrを使用してコピーを許可することは、一種のアンチパターンまたは悪いことだと思いました。

これは正しいです?

52
Clinton

Pimplイディオムを使用していくつかのオブジェクトを作成してきましたが、shared_ptrunique_ptrのどちらを使用するかわかりません。

間違いなくunique_ptrまたはscoped_ptr

Pimplはパターンではありませんが、コンパイル時の依存関係とバイナリ互換性を扱う慣用句です。特にコピー動作に関して、オブジェクトのセマンティクスに影響を与えるべきではありません。

どんな種類のスマートポインターを使用してもかまいませんが、コピーコンストラクターと代入演算子の実装について意識的に決定する必要があるため、2つの異なるオブジェクト間で実装を誤って共有しないことが保証されます。

ただし、変更はすべてのコピーに影響するため、これらのオブジェクトは実際にはコピーされません。したがって、おそらくshared_ptrを使用してコピーを許可することは、一種のアンチパターンまたは悪いことだと思いました。

これはアンチパターンではありません。実際、エイリアスです。あなたはすでにそれをC++で使用していて、裸のポインタと参照を持っています。 shared_ptrは、余分な複雑さと新しい問題(メモリリークを発生させるサイクルに注意)を犠牲にして、デッドリファレンスを回避するための「安全性」の追加測定を提供します。


Pimplとは関係ありません

unique_ptrの方が効率的ですが、これらのオブジェクトはとにかく重いため、shared_ptrunique_ptrに比べてコストが比較的小さいため、これはそれほど問題ではありません。

一部の状態を除外できる場合は、 Flyweight パターンを確認することをお勧めします。

36
Matthieu M.

shared_ptrを使用する場合、それは実際には古典的なpimplイディオムではありません(追加の手順を実行しない限り)。しかし、実際の問題は、なぜスマートポインタを使用して始めたいのかということです。 deleteがどこで発生するかは非常に明確であり、例外の安全性やその他の問題はありません。せいぜい、スマートポインターは1行または2行のコードを節約します。そして、正しいセマンティクスを持つ唯一のものはboost::scoped_ptrであり、この場合は機能しないと思います。 (IIRC、インスタンス化するには完全な型が必要ですが、間違っている可能性があります。)

Pimplイディオムの重要な側面は、その使用がクライアントに対して透過的であることです。クラスは、古典的に実装されているかのように正確に動作する必要があります。これは、クラスが不変(非constメンバー関数がない)でない限り、コピーと割り当てを禁止するか、ディープコピーを実装することを意味します。通常のスマートポインターはいずれもディープコピーを実装していません。もちろん、実装することもできますが、コピーが発生するたびに完全な型が必要になる可能性があります。つまり、ユーザー定義のコピーコンストラクターと代入演算子を提供する必要があります(インラインにできないため)。このことを考えると、スマートポインターを使用する手間はおそらく必要ありません。

例外は、オブジェクトが不変である場合です。この場合、コピーが深いかどうかは関係なく、shared_ptrは状況を完全に処理します。

10
James Kanze

shared_ptrを使用する場合(たとえば、コンテナーで、これを調べてそれを返すby-value)、それが指すオブジェクトのコピーを引き起こさず、単に参照カウントを持つポインタのコピー。

つまり、基になるオブジェクトを複数のポイントから変更すると、同じインスタンスの変更に影響します。これはまさにそのために設計されたものであるため、一部のアンチパターンではありません。

shared_ptrを渡すとき(コメントのとおり)、必要に応じてconst参照で渡し、(参照カウントをインクリメントして)コピーすることをお勧めします。返品はケースバイケースです。

5
Nim

はい、ご利用ください。簡単に言えば、shared_ptrはスマートポインタの実装です。 unique_ptrは自動ポインタの実装です:

0
Trombe