web-dev-qa-db-ja.com

shared_ptrを使用する場合とrawポインターを使用する場合

class B;

class A
{
public:
    A ()
        : m_b(new B())
    {
    }

    shared_ptr<B> GimmeB ()
    {
        return m_b;
    }

private:
    shared_ptr<B> m_b;
};

BがAのライフタイム以外に意味的に存在してはならないクラスであるとしましょう。つまり、Bが単独で存在することはまったく意味がありません。 GimmeBshared_ptr<B>またはB*を返す必要があります

一般的に、スマートポインターの代わりに、C++コードで生のポインターを完全に使用しないことをお勧めしますか?

shared_ptrは、所有権の明示的な転送または共有がある場合にのみ使用されるべきであると考えています。これは、関数がメモリを割り当て、データを入力し、それを返す場合を除いて、非常にまれだと思います、呼び出し元と呼び出し先の間に、前者がそのデータに対して「責任」を負うことを理解しています。

71
TripShock

shared_ptrをいつ使用し、いつ生ポインタを使用すべきか」という質問。非常に簡単な答えがあります:

  • ポインタに所有権を付加したくない場合は、生のポインタを使用します。多くの場合、このジョブは参照を使用して実行することもできます。ローポインターは、低レベルのコード(スマートポインターの実装やコンテナーの実装など)でも使用できます。
  • オブジェクトの一意の所有権が必要な場合は、unique_ptrまたはscope_ptrを使用します。これは最も便利なオプションであり、ほとんどの場合に使用する必要があります。一意の所有権は、ポインタを使用するのではなく、オブジェクトを直接作成することでも表現できます(できれば、unique_ptrを使用するよりも優れています)。
  • ポインターの所有権を共有する場合は、shared_ptrまたはintrusive_ptrを使用します。これは混乱を招き、非効率的であり、多くの場合、良い選択肢ではありません。所有権の共有は、一部の複雑な設計では役立ちますが、理解しにくいコードにつながるため、一般的には避ける必要があります。

shared_ptrsはrawポインターとはまったく異なるタスクを実行し、shared_ptrsもrawポインターもほとんどのコードにとって最適なオプションではありません。

25
Mankarse

良い経験則は次のとおりです。

  • 所有権の参照の転送または共有がない場合、またはプレーンポインタで十分です。 (プレーンポインターは参照よりも柔軟です。)
  • 所有権の譲渡があるが共有所有権がない場合、std::unique_ptr<>が適切な選択です。多くの場合、工場出荷時の機能があります。
  • 所有権が共有されている場合、std::shared_ptr<>またはboost::intrusive_ptr<>の適切な使用例です。

コピーの点で最も高価であり、std::shared_ptr<>はプレーンポインターのストレージの2倍の容量を必要とすることもありますが、最も重要なことは、所有者を消去すると、互いに共有されたポインタを保持しているために破壊できないオブジェクトの毛玉になります。

最適な設計は、明確な所有権が確立され、階層的であるため、理想的には、スマートポインターはまったく必要ありません。たとえば、一意のオブジェクトを作成する、または既存のオブジェクトを返すファクトリがある場合、作成したオブジェクトをファクトリが所有し、値によって連想コンテナ(std::unordered_mapなど)に保持することは理にかなっています。ユーザーにプレーンポインターまたは参照を返すことができること。このファクトリには、最初のユーザーの前に開始し、最後のユーザー(階層プロパティ)の後に終了するライフタイムが必要です。これにより、ユーザーは既に破棄されたオブジェクトへのポインターを持つことができません。

10

GimmeB()の呼び出し先が、Aのインスタンスが終了した後、ptrのコピーを保持することでポインターの寿命を延長できるようにしたくない場合、間違いなくshared_ptrを返すべきではありません。

呼び出し先が返されたポインターを長期間保持することになっていない場合、つまり、ポインターの前にAのライフタイムのインスタンスが期限切れになるリスクがない場合、生のポインターの方が良いでしょう。しかし、実際の生のポインターを使用する正当な理由がない限り、より良い選択は単に参照を使用することです。

最後に、Aインスタンスの有効期限が切れた後に返されたポインターが存在できる場合、ポインター自体がBの有効期限を延長したくない場合は、テストに使用できるweak_ptrを返すことができますまだ存在するかどうか。

肝心なのは、通常、生のポインターを使用するよりも優れたソリューションがあるということです。

6
reko_t

リソースの明示的な共有が発生する場合、shared_ptrが最適に使用されるというあなたの意見に同意しますが、他の種類のスマートポインターも利用できます。

あなたの正確な場合:参照を返さないのはなぜですか?

ポインターは、データがnullである可能性があることを示唆していますが、ここではBAが常に存在するため、nullになることはありません。参照はこの動作をアサートします。

そうは言っても、非共有環境でさえshared_ptrの使用を提唱し、weak_ptrハンドルを与える人々を、アプリケーションを「確保」して古いポインターを避けるという考えで見ました。残念ながら、shared_ptrからweak_ptrを復元できるため(実際にデータを操作する唯一の方法です)、これは意図されていなくても共有所有権のままです。

注:shared_ptrには微妙なバグがあります。Aのコピーは、デフォルトでは元のBと同じものを共有します。コピーコンストラクターとコピー割り当て演算子を明示的に記述します。そしてもちろん、Aを保持するためにBで生のポインターを使用しないでしょう、あなたはそうします:)?


もちろん、別の質問は、実際にそうする必要があるかどうかです。優れたデザインの原則の1つは、encapsulationです。カプセル化を実現するには:

内部にハンドルを返さないでください( デメテルの法則 を参照)。

したがって、おそらくあなたの質問に対する本当の答えは、Bへの参照またはポインタを渡すのではなく、Aのインターフェイスを介してのみ変更する必要があるということです。

3
Matthieu M.

一般に、生のポインタは非常に曖昧な意味を持っているため、可能な限り生のポインタを使用することは避けます。そして、ドキュメントは常に悪い、時代遅れ、または誤解されています。

所有権が問題になる場合は、スマートポインターを使用します。そうでない場合、実行可能な場合は参照を使用します。

2
thiton
  1. Aの構築時にBを割り当てます。
  2. あなたは、BがAsライフタイムの外側で存続すべきでないと言います。
    これらは両方とも、BがAのメンバーであり、参照アクセサを返すだけであることを示しています。これを過剰に設計していますか?
2
Ricibob

C++コアガイドラインは、この質問に対して非常に有用なヒントを提供していることがわかりました。

Rawポインター(T *)またはよりスマートなポインターを使用するかどうかは、オブジェクトの所有者(objのメモリを解放する責任)に依存します。

自分の :

smart pointer, owner<T*>

所有していない:

T*, T&, span<>

owner <>、span <>はMicrosoft GSLライブラリで定義されています

経験則は次のとおりです。

1)所有権を渡すために未加工のポインター(または独自の型ではない)を使用しない

2)スマートポインターは、所有権のセマンティクスが意図されている場合にのみ使用してください

3)T *または所有者が個々のオブジェクトを指定する(のみ)

4)配列にvector/array/spanを使用

5)誰にもわからないが、shared_ptrは通常、誰がobjをリリースするかわからない場合に使用されます。たとえば、1つのobjがマルチスレッドで使用されます

2
camino

生のポインタを使用しないようにすることをお勧めしますが、すべてをshared_ptrに置き換えることはできません。この例では、クラスのユーザーは、Bの有効期間をAの有効期間より長くしてもかまわないと想定し、独自の理由で返されたBオブジェクトをしばらく保持することを決定できます。 weak_ptrを返す必要があります。または、Aが破棄されたときにBが絶対に存在できない場合は、Bへの参照または単に生のポインターを返します。

1
hamstergene

あなたが言うとき:「Bが意味的にAの寿命の外に存在してはならないクラスだとしましょう」

これは、Bが論理的に Aなしでは存在してはならないことを示していますが、物理的にはどうですか? A dtorsの後に* Bを使用しようとする人が誰もいないことが確実な場合は、おそらく生のポインターで十分です。それ以外の場合は、よりスマートなポインターが適切です。

クライアントがAへの直接ポインタを持っている場合、クライアントはそれを適切に処理することを信頼する必要があります。保管しようとしないなど。

0
seand