8

これは、パフォーマンスの問題というよりもスタイリングの問題です。ポインター (ほとんど) を shared_ptr オブジェクトに変換したばかりで、生のポインターの代わりとしてしぶしぶ weak_ptrs を受け入れるようになりました。私の質問は、共有ポインター オブジェクトのシーケンス (ベクトルとしましょう) を反復処理するための推奨される方法は何ですか? これが私がやっていることです:

std::vector<std::shared_ptr<A>> my_sequence;
// Do something to fill my_sequence;

for (std::shared_ptr<A> const& ptr : my_sequence)
{
  ptr->AMethod();
}

ただし、これは *shared_ptr 参照を使用しない* ルールに反します。

私が尋ねる質問は次のとおりです。手法は堅牢ですか。AMethod() が非常に小さく、my_sequence が非常に大きい場合、shared_ptr のコピーが原因で、このメソッドがパフォーマンスを不必要に妨げ始めるのでしょうか? それは読めますか?シンプルですか?

4

2 に答える 2

8

shared_ptr 参照を使用しない理由は、自分自身を保護しようとしているメカニズムを無効にするためです。つまり、ダングリング ポインターが存在する可能性があります。コンテナーを反復処理している場合、オブジェクトが予期せず消えないため、これは問題になりません。後で使用する可能性のある shared_ptr 参照を保存しないでください。

すなわちこれは悪いです:

struct Y
{
    int y;
    Y() : y(1) {}
};

struct X
{
     shared_ptr<Y>& ref_shared_ptr_y;
     X(shared_ptr<Y>& y) : ref_shared_ptr_y(y) {}
};

void main()
{
    shared_ptr<Y> shared_ptr_y(new Y());
    X x(shared_ptr_y);
    y.reset();
    x.ref_shared_ptr_y->y;  // UB since x.ref_shared_ptr_y was deleted
}

shared_ptrオブジェクトの所有権を 2 つ以上の場所で共有する必要がある場合にのみ使用してください。そうしないと、不必要なオーバーヘッドが発生し、所有者の関係について十分に考えていないことがわかります。所有権が 1 つの場所に限定されている場合は、unique_ptr.

于 2013-06-24T23:57:17.730 に答える
8

まず、免責事項:

shared_ptr万能薬ではありません。所有権が実際に共有されている場合に使用する必要があります。非共有の所有権は (生の) 参照によって表されunique_ptr、非所有権は (生の) 参照によって表されます。weak_ptrは、監視shared_ptrする必要があるが所有されていない a 用です...しかし、一般的に古いポインターに対する適切な防御方法ではありません。デフォルトの toshared_ptrは、最小公分母に直接適用されます。それは貧弱なプログラミングの練習です。


ここでの選択肢はshared_ptr< T >、 、shared_ptr< T const >shared_ptr< T > const &T const &およびT &です。何も変更していないと仮定しましょうが、シーケンスはそのオブジェクトを変更する可能性constがあるため、望ましいことです。shared_ptr< T const >shared_ptr< T > const &、およびに絞り込みT const &ます。

質問を次のように再定式化しましょう。

shared_ptr< T > const &正しい選択はありますか?

この観点から、代替案を評価します。

  • shared_ptr< T const >(共有ポインタを値で渡す)

    • +: 単純な値のセマンティクス。合格できますshared_ptr< T >
    • +: コピーまたはmoved して所有権プールを拡張できます
    • +:const呼び出された関数に正確性を伝播します
    • +: オブジェクトへのアクセスが速い: 直接ポインタ引数が含まれている
    • –: 渡すのはコストがかかる: 2 つの機械語をコピーし、ref カウントをアトミックタッチする
    • –: 呼び出された関数は、所有権のセマンティクスに関係しています
  • shared_ptr< T > const &(共有ポインタを参照渡し)

    • +: 受け渡しは直接オブジェクト参照と同じくらい安価です
    • +: 所有権プールを拡張するためにコピーできます
    • –:const呼び出された関数の正確性が失われます

      • 代わりに試してみるとshared_ptr< T const > const &、一時的なコピーが渡され、この代替手段と値渡しの欠点が生じます。
    • –: オブジェクトへのアクセスは余分な間接化を経由します

    • –: 呼び出された関数は、所有権のセマンティクスに関係しています
  • T const &(直接参照)

    • +:const正確性を維持
    • +: 所有権のセマンティクスに関係のない呼び出し元
    • +: 機械語を 1 つだけ、高速で渡します
    • +: 関数への高速な直接ポインタ引数にアクセスします (それさえあれば)
    • –: 所有権を付与できません

バランスをとって、所有権が拡張されていない場合 (引数を保持するオブジェクトを返すなど)、単純な参照を渡す必要があります。そのような場合、関数はその引数がどのように所有されているかを気にする必要はほとんどありません。それを必要とshared_ptrすることは、最悪の場合、懸念の分離に違反します。ローカル オブジェクトまたはメンバー サブオブジェクトを保持したまま関数を使用することはできません。そして何よりも、すべてがより複雑です。

所有権が他の誰かに拡張される可能性がある場合、それがshared_ptr最初に使用する正当な理由となる場合は、常に値渡しする必要がありshared_ptr< [const] T >ます。はい、オンコールをコピーしshared_ptrます。しかし、a をコピーすることのコストのかかる部分はshared_ptr、refcount を更新することであり、ポインターをスタックにプッシュすることではありません。また、所有権を譲渡する場合は、とにかく refcount を更新する必要があります。渡された値とmove、refcount に触れない値を取得します。これを行うと、参照渡しには利点がなく、多くの欠点が残ります。

于 2013-06-28T01:10:43.513 に答える