web-dev-qa-db-ja.com

std :: shared_ptr:reset()と割り当て

これは基本的な質問ですが、それに関する以前の投稿は見つかりませんでした。次の質問のタイトルは、私の質問と同じ質問のように聞こえますが、質問自体はタイトルと一致しません: shared_ptr.resetまたはoperator =?

_std::shared_ptr_のreset()メンバー関数の目的について混乱しています:代入演算子に加えて何が貢献していますか?

具体的には、定義が与えられた場合:

_auto p = std::make_shared<int>(1);
_
  1. 次の2行は同等ですか:

    _p = std::make_shared<int>(5);
    p.reset(new int(5));
    _
  2. これらはどうですか:

    _p = nullptr;
    p.reset();
    _

両方のケースで2行が同等である場合、reset()の目的は何ですか?


編集:質問の言い回しを変えて、そのポイントをより強調します。問題は、reset()を使用すると、それなしではそれほど簡単に達成できないことを達成できる場合があるかどうかです。

37
AlwaysLearning

reset()を使用する場合、resetに渡されるパラメーターは管理対象オブジェクトである必要はありません(そうすることもできません)。一方、_=_の場合、右側は管理対象オブジェクトでなければなりません。

したがって、これらの2行は同じ結果になります。

_p = std::make_shared<int>(5); // assign to a newly created shared pointer
p.reset(new int(5)); // take control of a newly created pointer
_

ただし、次のことはできません。

_p = new int(5); // compiler error no suitable overload
p.reset(std::make_shared<int>(5).get()); // uh oh undefined behavior
_

reset()がなければ、共有ポインターを作成して割り当てることなしに、共有ポインターを別の生のポ​​インターに再割り当てすることはできません。 _=_がなければ、共有ポインターが別の共有ポインターを指すようにすることはできません。

24
NathanOliver

resetが特定の場合に動的メモリ割り当てを回避することは可能です。コードを検討する

std::shared_ptr<int> p{new int{}};  // 1
p.reset(new int{});                 // 2

1行目には、2つの動的メモリ割り当てが発生します。1つはintオブジェクト用で、もう1つはshared_ptrの制御ブロック用です。オブジェクト。

2行目には、新しいintオブジェクトの動的メモリ割り当てが再びあります。 resetの本体内で、shared_ptrは、以前に管理されていたintへの他の強力な参照がないと判断するため、deleteにする必要があります。弱参照も存在しないため、制御ブロックの割り当てを解除することもできますが、この場合は、とにかく新しい制御ブロックを割り当てる必要があるため、同じ制御ブロックを再利用するのが賢明です。

常に割り当てを使用する必要がある場合、上記の動作は不可能です。

std::shared_ptr<int> p{new int{}};    // 1
p = std::shared_ptr<int>{new int{}};  // 2

この場合、2行目のshared_ptrコンストラクターへの2回目の呼び出しで既に制御ブロックが割り当てられているため、pは既存の制御ブロックの割り当てを解除する必要があります。

6
Praetorian

make_sharedを介した構築またはポインターからの構築の違いに関する最初のサブ質問の背後にある理論的根拠は含めません。この違いは この素晴らしい質問

ただし、resetoperator=の使用を区別することは建設的だと思います。前者は、shared_ptrがたまたま唯一の所有者である場合に破棄するか、参照カウントを減らすことにより、shared_ptrによって管理されるリソースの所有権を放棄します。後者は、別のshared_ptrと所有権を共有することを意味します(構築中の場合を除きます)。

コメントで述べたように、resetに渡されたポインターは、別の共有または一意のポインターによって所有されないことが重要です。 deleteリソースに。

resetの使用例の1つは、共有リソースの遅延初期化です。本当に必要な場合にのみ、shared_ptrでリソース(メモリなど)を管理する必要があります。次のような完全な割り当てを行います。

std::shared_ptr<resource> shared_resource(new resource(/*construct a resource*/));

実際に必要とされない場合は無駄です。遅延初期化でこれを行うには、次のようなものが適用されます。

std::shared_ptr<resource> shared_resource;
void use_resource()
{
       if(!shared_resource)
       {
            shared_resource.reset(new resource(...));
       }

       shared_resource->do_foo();
}

この場合のresetの使用は、swapを実行したり、一時的なshared_ptrに割り当てるよりも簡潔です。

2
Alejandro

reset()は、既存のshared_ptrの管理対象オブジェクトを変更します。

p = std :: shared_ptr(new int(5));およびp.reset(new int(5));

前者は、新しいshared_ptrを作成し、それを変数に移動することを伴います。後者は、新しいオブジェクトを作成するのではなく、shared_ptrによって管理される基になるポインターを変更するだけです。

別の言い方をすれば、この2つは異なるケースで使用されることを意味しています。割り当ては、shared_ptrおよびresetがある場合に使用し、生のポインターがある場合に使用します。

留意すべきもう1つのことは、shared_ptrがブーストで利用可能で、移動割り当てが存在し、最新バージョンに大きな影響を与えていたということです。移動の割り当てがなければ、コピーを作成せずにshared_ptrを変更できると、余分なオブジェクトの記録を保存できるため、有益です。

1
Guvante