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が単独で存在することはまったく意味がありません。 GimmeB
はshared_ptr<B>
またはB*
を返す必要があります
一般的に、スマートポインターの代わりに、C++コードで生のポインターを完全に使用しないことをお勧めしますか?
shared_ptr
は、所有権の明示的な転送または共有がある場合にのみ使用されるべきであると考えています。これは、関数がメモリを割り当て、データを入力し、それを返す場合を除いて、非常にまれだと思います、呼び出し元と呼び出し先の間に、前者がそのデータに対して「責任」を負うことを理解しています。
「shared_ptr
をいつ使用し、いつ生ポインタを使用すべきか」という質問。非常に簡単な答えがあります:
unique_ptr
またはscope_ptr
を使用します。これは最も便利なオプションであり、ほとんどの場合に使用する必要があります。一意の所有権は、ポインタを使用するのではなく、オブジェクトを直接作成することでも表現できます(できれば、unique_ptr
を使用するよりも優れています)。shared_ptr
またはintrusive_ptr
を使用します。これは混乱を招き、非効率的であり、多くの場合、良い選択肢ではありません。所有権の共有は、一部の複雑な設計では役立ちますが、理解しにくいコードにつながるため、一般的には避ける必要があります。shared_ptr
sはrawポインターとはまったく異なるタスクを実行し、shared_ptr
sもrawポインターもほとんどのコードにとって最適なオプションではありません。
良い経験則は次のとおりです。
std::unique_ptr<>
が適切な選択です。多くの場合、工場出荷時の機能があります。std::shared_ptr<>
またはboost::intrusive_ptr<>
の適切な使用例です。コピーの点で最も高価であり、std::shared_ptr<>
はプレーンポインターのストレージの2倍の容量を必要とすることもありますが、最も重要なことは、所有者を消去すると、互いに共有されたポインタを保持しているために破壊できないオブジェクトの毛玉になります。
最適な設計は、明確な所有権が確立され、階層的であるため、理想的には、スマートポインターはまったく必要ありません。たとえば、一意のオブジェクトを作成する、または既存のオブジェクトを返すファクトリがある場合、作成したオブジェクトをファクトリが所有し、値によって連想コンテナ(std::unordered_map
など)に保持することは理にかなっています。ユーザーにプレーンポインターまたは参照を返すことができること。このファクトリには、最初のユーザーの前に開始し、最後のユーザー(階層プロパティ)の後に終了するライフタイムが必要です。これにより、ユーザーは既に破棄されたオブジェクトへのポインターを持つことができません。
GimmeB()の呼び出し先が、Aのインスタンスが終了した後、ptrのコピーを保持することでポインターの寿命を延長できるようにしたくない場合、間違いなくshared_ptrを返すべきではありません。
呼び出し先が返されたポインターを長期間保持することになっていない場合、つまり、ポインターの前にAのライフタイムのインスタンスが期限切れになるリスクがない場合、生のポインターの方が良いでしょう。しかし、実際の生のポインターを使用する正当な理由がない限り、より良い選択は単に参照を使用することです。
最後に、Aインスタンスの有効期限が切れた後に返されたポインターが存在できる場合、ポインター自体がBの有効期限を延長したくない場合は、テストに使用できるweak_ptrを返すことができますまだ存在するかどうか。
肝心なのは、通常、生のポインターを使用するよりも優れたソリューションがあるということです。
リソースの明示的な共有が発生する場合、shared_ptr
が最適に使用されるというあなたの意見に同意しますが、他の種類のスマートポインターも利用できます。
あなたの正確な場合:参照を返さないのはなぜですか?
ポインターは、データがnullである可能性があることを示唆していますが、ここではB
にA
が常に存在するため、nullになることはありません。参照はこの動作をアサートします。
そうは言っても、非共有環境でさえshared_ptr
の使用を提唱し、weak_ptr
ハンドルを与える人々を、アプリケーションを「確保」して古いポインターを避けるという考えで見ました。残念ながら、shared_ptr
からweak_ptr
を復元できるため(実際にデータを操作する唯一の方法です)、これは意図されていなくても共有所有権のままです。
注:shared_ptr
には微妙なバグがあります。A
のコピーは、デフォルトでは元のB
と同じものを共有します。コピーコンストラクターとコピー割り当て演算子を明示的に記述します。そしてもちろん、A
を保持するためにB
で生のポインターを使用しないでしょう、あなたはそうします:)?
もちろん、別の質問は、実際にそうする必要があるかどうかです。優れたデザインの原則の1つは、encapsulationです。カプセル化を実現するには:
内部にハンドルを返さないでください( デメテルの法則 を参照)。
したがって、おそらくあなたの質問に対する本当の答えは、B
への参照またはポインタを渡すのではなく、A
のインターフェイスを介してのみ変更する必要があるということです。
一般に、生のポインタは非常に曖昧な意味を持っているため、可能な限り生のポインタを使用することは避けます。そして、ドキュメントは常に悪い、時代遅れ、または誤解されています。
所有権が問題になる場合は、スマートポインターを使用します。そうでない場合、実行可能な場合は参照を使用します。
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がマルチスレッドで使用されます
生のポインタを使用しないようにすることをお勧めしますが、すべてをshared_ptr
に置き換えることはできません。この例では、クラスのユーザーは、Bの有効期間をAの有効期間より長くしてもかまわないと想定し、独自の理由で返されたBオブジェクトをしばらく保持することを決定できます。 weak_ptr
を返す必要があります。または、Aが破棄されたときにBが絶対に存在できない場合は、Bへの参照または単に生のポインターを返します。
あなたが言うとき:「Bが意味的にAの寿命の外に存在してはならないクラスだとしましょう」
これは、Bが論理的に Aなしでは存在してはならないことを示していますが、物理的にはどうですか? A dtorsの後に* Bを使用しようとする人が誰もいないことが確実な場合は、おそらく生のポインターで十分です。それ以外の場合は、よりスマートなポインターが適切です。
クライアントがAへの直接ポインタを持っている場合、クライアントはそれを適切に処理することを信頼する必要があります。保管しようとしないなど。