web-dev-qa-db-ja.com

lock()とexpired()の違いは何ですか? weak_ptr C ++

最近、C++ 11から始めました。 _weak_ptr_について勉強しました。生のポインタを取得する方法は2つあります。

  1. lock()関数

    _shared_ptr<Foo> spFoo = wpPtr.lock();
    
    if(spFoo) {
        spFoo->DoSomething();
    }
    _
  2. expired()関数

    _if(!wpPtr.expired())
    {
        shared_ptr<Foo> spFoo = wpPtr.lock();
        spFoo->DoSomething();
    }
    _

どちらが良い方法ですか? 2つの方法の違いは何ですか?

13
zeno

したがって、共有ptrとweak ptrはスレッドセーフであり、特定のスレッドにローカルなオブジェクトのインスタンスがあり、それらが共通のポイントされたオブジェクトを共有している場合、あるスレッドと別のスレッドでそれらと対話でき、すべてが機能します。

これが正しく機能するためには、それらを適切に使用する必要があります。

wp.expired()は、「期限切れの弱いptrをすべてバッファから削除する」などの場合にのみ役立ちます。それはあなたがそれを置く目的には役に立ちません。

すべての弱いポインタは、一度期限切れになると、期限切れのままになります。ただし、エンゲージされた弱いポインタは、エンゲージされていることを確認した後、すぐに期限切れになる可能性があります。

_if(!wpPtr.expired())  {
  // <<--- here
  shared_ptr<Foo> spFoo = wpPtr.lock();
  spFoo->DoSomething();
}
_

_<<--- here_では、マルチスレッド環境でのwpPtrの状態についてnothingがわかります。有効期限が切れている場合とそうでない場合があります。一方:

_if(wpPtr.expired())  {
  // <<--- there
}
_

_<<--- there_で、私たちはdo弱いポインタが期限切れになっていることを知っています。

File ioや他の種類の「トランザクション」操作と同様に、何かができるかどうかを確認する唯一の方法はやってみてくださいです。実行できるはずだと判断してから実行するまでの間に、状態が変化して操作が失敗する可能性があります。

ほぼ確実にできなかった早い段階で解決できる場合があります。これは便利な場合もありますが、試してみるまで実行できるかどうかはわかりません。試行は失敗する可能性があり、その時点でエラーを処理します。

_if(auto spFoo = wpPtr.lock())  {
  spFoo->DoSomething();
}
_

これは、弱いポインタと対話するための「正しい」方法です。弱いポインターの有効性をテストし、同じ操作で共有ポインターを取得します。

if()ヘッダーの外側にspFooを作成することは許容されますが、spFooのスコープは有効なゾーンに正確に制限されるため、この手法をお勧めします。

他の好ましい手法は、SFINAEに適したコードを記述した早期終了です。

_auto spFoo = wpPtr.lock();

if(!spFoo) return error("wp empty");

spFoo->DoSomething();
_

これにより、コードフローの「予想される」実行が、インデント、条件、またはジャンプなしでフラットラインになります。

2番目のバリアントには2つの問題があります。

  1. 不要なチェックを行いますwpPtr.expired()
  2. spFooを逆参照する前に必要なチェックif (spFoo)がありません。

最初のバリアントはトランザクション型であり、最終的にウィークポインターによって参照されるオブジェクトを操作する必要がある場合に使用します。

5
bobah

オプション1

オプション2を選択した場合、wpPtr.expired()の呼び出しとwpPtr.lock()の呼び出しの間に_weak_ptr_の有効期限が切れている可能性があり、行spFoo->DoSomething()はnullを逆参照しようとします。 _shared_ptr_。

2
Chris Drew

以下は、_weak_ptr_に関連する操作です。アプローチ2はスレッドセーフではないため、オプション1を使用する必要があります。

w.use_count()wと所有権を共有する_shared_ptr_の数

w.expired()は、w.use_count()がゼロの場合はtrueを返し、それ以外の場合はfalseを返します。

w.lock()expiredtrueの場合、nullを返します_shared_ptr_;それ以外の場合は、wが指すオブジェクトに_shared_ptr_を返します。

(2)スレッドセーフではありません

_// let p be the last shared_ptr pointing at the same object as wpPtr
if (!wpPtr.expired())
{
    // we enter the if-statement because wpPtr.use_count() is 1
    // p goes out of scope on its thread, the object gets deleted
    shared_ptr<Foo> spFoo = wpPtr.lock(); // null shared_ptr
    spFoo->DoSomething(); // ERROR! deferencing null pointer
}
_

(1)スレッドセーフ

_// let p be the last shared_ptr pointing at the same object as wpPtr
shared_ptr<Foo> spFoo = wpPtr.lock();
// now, wpPtr.use_count() is 2, because spFoo and p are both pointing at the object
// p goes out of scope on its thread, but spFoo is still pointing at the object
if(spFoo) {
    spFoo->DoSomething(); // OK! safe to dereference
}
_
1
sam

cppreference.com から引用するには:

std::weak::lockは効果的に

expired() ? shared_ptr<T>() : shared_ptr<T>(*this)

基になるオブジェクトが有効かどうかを確認するためにexpiredを使用し、オブジェクトをstd::shared_ptrにプロモートする可能性があるようにロックします

0
doron