2つのクラスがあると仮定します。
class A
{
...
}
class B : public A
{
...
}
書くほうがいいですか
std::deque<shared_ptr<A> > container;
または
std::deque<reference_wrapper<A> > container;
これらのクラスの両方への参照を含めることができるコンテナを作成する方法とその理由
私の場合、参照が何らかの理由で無効になった場合、コンテナを含むクラスは気にしません。
ただし、他の方法よりも1つの方法を選択する場合に考慮すべき注意点はありますか?
これは私の問題についての私の意見です:
両方のバリアントを実際に使用できるかどうかを検討してください。 _reference_wrapper
_は、設計上、デフォルトでは構築できません。つまり、たとえば、参照ラッパーを使用しているときにcontainer.resize()
を呼び出すことができなくなります。一方、_shared_ptr
_はデフォルトで無効/ NULL状態に構築されます。したがって、一部のユースケースでは、_reference_wrapper
_を使用することはできません。いくつかの例を挙げます:
_int i = 42;
std::reference_wrapper<int> iref(i); // OK
std::reference_wrapper<int> uninitialized_iref; // Not OK, but OK with shared_ptr
iref = 23; // Not OK, but OK on a "normal" reference
int& iref2 = iref; iref2 = 32; // OK
static_cast<int&>(iref) = 23; // OK
iref.get() = 23; // OK
std::vector<std::reference_wrapper<int> > irefs; // OK
irefs.resize(10); // Not OK!
irefs.resize(10, i); // OK
_
上記の例は、すでに別のことを示唆しています:使いやすさと読みやすさ。ユースケースに応じて、ポインタ形式の表記法(_shared_ptr
_)を使用するか、参照形式の表記法(_reference_wrapper
_)を使用するか、およびそれによりコードを保守しやすくするかを決定する必要があります。 。個人的に私はこの側面を非常に高く評価しています。
質問へのコメントで指摘されているように、オブジェクトの存続期間も影響します。コンテナのライフタイムが参照されるオブジェクトのライフタイムを超えることが意図されている場合、あなたはhave_shared_ptr
_を使用するか、または別のシステム設計)。リファレンスでは、それぞれのスコープについて確認する必要があります。私はまた、あなたの特定のケースでは、あなたが実際に参照が無効になることに注意する必要があると思います-それが起こる可能性がある場合、あなたは問題を抱えているでしょう、そして通常、デバッグは困難です。
もちろん、_shared_ptr
_要素を格納するということは、参照する値がalreadyが共有ポインタに格納されていることを意味します。他の方法で作成された要素を参照する共有ポインタは作成できません。したがって、既存、非共有要素を参照する必要がある場合、_shared_ptr
_は使用できず、_reference_wrapper
_または単純な古いポインタを使用する必要があります。
パフォーマンスに関しては、それほど大きな違いはないと思います。 _reference_wrapper
_は通常内部的にポインタを使用し、_shared_ptr
_のオーバーヘッドは通常無視できます。
全体として、私は(a)私の具体的なユースケース、および(b)コンテナーを使用するコードの使いやすさ/保守性/読みやすさに基づいて決定を下します。
コンテナに入れるオブジェクトの所有者については触れません。 shared_ptr
はオプションであるため、おそらく何らかの形式の共有所有権と動的ストレージ割り当てがあります。 オブジェクトの所有者とオブジェクトの監視方法(つまり、オブジェクトを監視できるユーザー)の明確な定義は、実装の大部分を構成します。
私の特定のケースでは、コンテナを含むクラスは、参照が何らかの理由で無効になったとしても、実際には気にしませんでした。
コンテナはオブザーバーの役割を果たしています。
ただし、一方のアプローチをもう一方のアプローチよりも使用する必要がある場合は、より一般化された答えと「経験則」があるかもしれません。
reference_wrapper
のセマンティクスを考えると、reference_wrapper
が有効である一方で、参照されているオブジェクトが有効であることが合理的に期待されています。あなたのケースではこれは当てはまらないかもしれませんが、一般にこれは真実であり、後日コードで行われるメンテナンスはこれを想定しているでしょう。
共有所有権オブジェクトの存続期間をstd::shared_ptr
で管理できるようにし、コンテナで「オブザーバー」std::weak_ptr
を使用します。
オブジェクトの存続期間が共有されておらず、何らかの理由で(自動ストレージ期間を介して)コンテナー自体の存続期間に関連付けられている場合、reference_wrapper
は実行可能です(同様の方法で、生のポインターになることもできます)。
それは主に生涯と所有権の問題です。 _reference_wrapper
_を使用するコンテナは、オブジェクトを所有したり、オブジェクトの寿命を管理したりしませんが、_shared_ptr
_を使用するコンテナは所有します。
コンテナーがそのオブジェクトを所有してはならない場合は、_reference_wrapper
_を使用します。
オブジェクトを所有する必要がある場合は、3番目のオプションを選択します。 Boost.Pointer Container には、 boost :: ptr_deque のようなテンプレートクラスがあり、多相型を格納し、所有権を管理します。_shared_ptr
_のオーバーヘッドや厄介な構文はありません各要素の_shared_ptr
_を逆参照する必要があります。