What is diffrence between lock() and expired()? weak_ptr C++

13,545

Solution 1

So shared ptr and weak ptr are thread safe, in that if you have an instance of the object local to a given thread, and they share a common pointed-to object, you can interact with them in one thread and another and everything works.

For this to work properly, you have to use them properly.

wp.expired() is only useful to do things like "remove every expired weak ptr from a buffer". It is not useful for the purpose you put it.

Every weak pointer, once expired, remains expired. But an engaged weak pointer can become expired immediately after you verify it is engaged.

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

At <<--- here we know nothing about the state of wpPtr in a multi-threaded environment. It could be expired or not expired. On the other hand:

if(wpPtr.expired())  {
  // <<--- there
}

At <<--- there we do know the weak pointer is expired.

Like with file io and other kinds of "transactional" operations, the only way to check if you can do something is try to do it. Between determining you should be able to do it and doing it, the state could change and the operation could fail.

You can sometimes work out that you almost certainly could not do it early, which is sometimes useful, but you cannot be certain you can do it until you try. The attempt to try can fail, at which point you handle the error.

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

this is the "right" way to interact with a weak pointer. Test for the validity of the weak pointer and get the shared pointer in the same operation.

Creating a spFoo outside of the if() header is acceptable, I prefer this technique as the scope of the spFoo is limited exactly to the zone where it is valid.

The other preferred technique is early exit:

auto spFoo = wpPtr.lock();

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

spFoo->DoSomething();

which makes the "expected" execution of the code flow in a flat line without indentation or conditions or jumps.

Solution 2

The second variant has two problems:

  1. It does an unnecessary check wpPtr.expired()
  2. It is missing a necessary check if (spFoo) before dereferencing spFoo

The first variant is transactional, and is the one to use when you ultimately need to work with the object referred to by the weak pointer.

Solution 3

Below are the relevant operations for a weak_ptr. You should go with option 1 because approach 2 is not thread-safe.

w.use_count() The number of shared_ptrs that share ownership with w

w.expired() returns true if w.use_count() is zero, false otherwise

w.lock() If expired is true, returns a null shared_ptr; otherwise returns a shared_ptr to the object to which w points.

(2) Not thread-safe

// 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) Thread-safe

// 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
}

Solution 4

Option 1.

If you go with option 2 then between calling wpPtr.expired() and calling wpPtr.lock() the weak_ptr could have expired and the line spFoo->DoSomething() will try to dereference a null shared_ptr.

Share:
13,545

Related videos on Youtube

zeno
Author by

zeno

Updated on July 14, 2022

Comments

  • zeno
    zeno almost 2 years

    Recently I started at C++11. I studied about weak_ptr. There exist two ways of getting raw pointer.

    1. lock() function

      shared_ptr<Foo> spFoo = wpPtr.lock();
      
      if(spFoo) {
          spFoo->DoSomething();
      }
      
    2. expired() function

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

    Which is the better way? What is different between the two ways?

    • Ben
      Ben about 3 years
      As others have said, you must use 1 to avoid a race. And to be concise, consider if (auto spFoo = wpFoo.lock()) { since then (short of an else block) nobody can access spFoo if it’s nullptr.
  • Yakk - Adam Nevraumont
    Yakk - Adam Nevraumont almost 8 years
    You dropped the important part of the quote. Which makes your quote so false I went to check to see how to fix cppreference. It turned out it was just your quoting that was wrong.
  • bartgol
    bartgol about 3 years
    I agree with Yakk. Indeed, the crucial point on cppreference is that the line you reported is executed atomically. That's why option 2 in the original question is only ok in a non-threaded context.