これは基本的な質問ですが、それに関する以前の投稿は見つかりませんでした。次の質問のタイトルは、私の質問と同じ質問のように聞こえますが、質問自体はタイトルと一致しません: shared_ptr.resetまたはoperator =?
_std::shared_ptr
_のreset()
メンバー関数の目的について混乱しています:代入演算子に加えて何が貢献していますか?
具体的には、定義が与えられた場合:
_auto p = std::make_shared<int>(1);
_
次の2行は同等ですか:
_p = std::make_shared<int>(5);
p.reset(new int(5));
_
これらはどうですか:
_p = nullptr;
p.reset();
_
両方のケースで2行が同等である場合、reset()
の目的は何ですか?
編集:質問の言い回しを変えて、そのポイントをより強調します。問題は、reset()
を使用すると、それなしではそれほど簡単に達成できないことを達成できる場合があるかどうかです。
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()
がなければ、共有ポインターを作成して割り当てることなしに、共有ポインターを別の生のポインターに再割り当てすることはできません。 _=
_がなければ、共有ポインターが別の共有ポインターを指すようにすることはできません。
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
は既存の制御ブロックの割り当てを解除する必要があります。
make_shared
を介した構築またはポインターからの構築の違いに関する最初のサブ質問の背後にある理論的根拠は含めません。この違いは この素晴らしい質問 。
ただし、reset
とoperator=
の使用を区別することは建設的だと思います。前者は、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
に割り当てるよりも簡潔です。
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
を変更できると、余分なオブジェクトの記録を保存できるため、有益です。