web-dev-qa-db-ja.com

ポリモーフィックテンプレートコンテナー:shared_ptrとreference_wrapper

2つのクラスがあると仮定します。

class A
{
    ...
}
class B : public A
{
    ...
}

書くほうがいいですか

std::deque<shared_ptr<A> > container;

または

std::deque<reference_wrapper<A> > container;

これらのクラスの両方への参照を含めることができるコンテナを作成する方法とその理由

私の場合、参照が何らかの理由で無効になった場合、コンテナを含むクラスは気にしません。

ただし、他の方法よりも1つの方法を選択する場合に考慮すべき注意点はありますか?

5
user108376

これは私の問題についての私の意見です:

  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
    _
  2. 上記の例は、すでに別のことを示唆しています:使いやすさと読みやすさ。ユースケースに応じて、ポインタ形式の表記法(_shared_ptr_)を使用するか、参照形式の表記法(_reference_wrapper_)を使用するか、およびそれによりコードを保守しやすくするかを決定する必要があります。 。個人的に私はこの側面を非常に高く評価しています。

  3. 質問へのコメントで指摘されているように、オブジェクトの存続期間も影響します。コンテナのライフタイムが参照されるオブジェクトのライフタイムを超えることが意図されている場合、あなたはhave_shared_ptr_を使用するか、または別のシステム設計)。リファレンスでは、それぞれのスコープについて確認する必要があります。私はまた、あなたの特定のケースでは、あなたが実際に参照が無効になることに注意する必要があると思います-それが起こる可能性がある場合、あなたは問題を抱えているでしょう、そして通常、デバッグは困難です。

  4. もちろん、_shared_ptr_要素を格納するということは、参照する値がalreadyが共有ポインタに格納されていることを意味します。他の方法で作成された要素を参照する共有ポインタは作成できません。したがって、既存非共有要素を参照する必要がある場合、_shared_ptr_は使用できず、_reference_wrapper_または単純な古いポインタを使用する必要があります。

  5. パフォーマンスに関しては、それほど大きな違いはないと思います。 _reference_wrapper_は通常内部的にポインタを使用し、_shared_ptr_のオーバーヘッドは通常無視できます。

全体として、私は(a)私の具体的なユースケース、および(b)コンテナーを使用するコードの使いやすさ/保守性/読みやすさに基づいて決定を下します。

4
mindriot

問題は所有権の一つです

コンテナに入れるオブジェクトの所有者については触れません。 shared_ptrはオプションであるため、おそらく何らかの形式の共有所有権と動的ストレージ割り当てがあります。 オブジェクトの所有者とオブジェクトの監視方法(つまり、オブジェクトを監視できるユーザー)の明確な定義は、実装の大部分を構成します

私の特定のケースでは、コンテナを含むクラスは、参照が何らかの理由で無効になったとしても、実際には気にしませんでした。

コンテナはオブザーバーの役割を果たしています。

ただし、一方のアプローチをもう一方のアプローチよりも使用する必要がある場合は、より一般化された答えと「経験則」があるかもしれません。

reference_wrapperのセマンティクスを考えると、reference_wrapperが有効である一方で、参照されているオブジェクトが有効であることが合理的に期待されています。あなたのケースではこれは当てはまらないかもしれませんが、一般にこれは真実であり、後日コードで行われるメンテナンスはこれを想定しているでしょう。

経験則

共有所有権オブジェクトの存続期間をstd::shared_ptrで管理できるようにし、コンテナで「オブザーバー」std::weak_ptrを使用します。

オブジェクトの存続期間が共有されておらず、何らかの理由で(自動ストレージ期間を介して)コンテナー自体の存続期間に関連付けられている場合、reference_wrapperは実行可能です(同様の方法で、生のポインターになることもできます)。

2
Niall

それは主に生涯と所有権の問題です。 _reference_wrapper_を使用するコンテナは、オブジェクトを所有したり、オブジェクトの寿命を管理したりしませんが、_shared_ptr_を使用するコンテナは所有します。

コンテナーがそのオブジェクトを所有してはならない場合は、_reference_wrapper_を使用します。

オブジェクトを所有する必要がある場合は、3番目のオプションを選択します。 Boost.Pointer Container には、 boost :: ptr_deque のようなテンプレートクラスがあり、多相型を格納し、所有権を管理します。_shared_ptr_のオーバーヘッドや厄介な構文はありません各要素の_shared_ptr_を逆参照する必要があります。

1
Josh Kelley