Home > Blockchain >  Why must the lock() function be used with a std::weak_ptr to safely extract the std::shared_ptr?
Why must the lock() function be used with a std::weak_ptr to safely extract the std::shared_ptr?

Time:08-24

Here's an example which shows two different ways of getting a shared_ptr from a weak_ptr:

#include <memory>
#include <iostream>

void print_shared1(std::weak_ptr <int> wp)
{
    // always gets pointer safely
    std::shared_ptr <int> sp(wp.lock());
    std::cout << "wp = " << (sp ? *sp : 0) << std::endl;
}

void print_shared2(std::weak_ptr <int> wp)
{
    // can crash if pointer has been freed
    std::shared_ptr <int> sp(wp);
    std::cout << "wp = " << (sp ? *sp : 0) << std::endl;
}

int main(int argc, char* argv[]) 
{
    std::shared_ptr <int> s = std::make_shared<int>(1);
    std::weak_ptr <int> w = s;

    print_shared1(w);
    print_shared2(w);

    s.reset();
    
    print_shared1(w);
    print_shared2(w);
}

When it is run, this is the output:

wp = 1
wp = 1
wp = 0
terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  bad_weak_ptr
Aborted (core dumped)

Clearly it is not always safe to access a weak pointer simply by constructing a shared pointer from it.

My questions are:

  1. In the function print_shared2, why are the semantics of wp.lock() not used during the construction of sp?
  2. Of what use is std::bad_weak_ptr at all?

CodePudding user response:

Of what use is std::bad_weak_ptr at all?

You just saw the use of it. lock returns an empty shared_ptr; the constructor throws an exception.

Using the shared_ptr constructor means "I am certain that this weak_ptr is valid, and if it isn't, that is an exceptional error condition that must be handled immediately." If you are uncertain of the status of the weak_ptr and want to check it, use lock.

CodePudding user response:

  1. There is the constructor std::shared_ptr<Y>::shared_ptr(const std::weak_ptr<Y>& r) (11) that allows doing it.
  2. While constructing a shared pointer it checks if (sp).

    Note that r.lock() may be used for the same purpose: the difference is that this constructor throws an exception if the argument is empty, while std::weak_ptr<T>::lock() constructs an empty std::shared_ptr in that case.

CodePudding user response:

Because that's how that std::shared_ptr constructor works. From [util.smartptr.shared.const]:

template<class Y> explicit shared_ptr(const weak_ptr<Y>& r);
    Constraints: Y* is compatible with T*.
    Effects: Constructs a shared_­ptr object that shares ownership with r and stores a copy of the pointer stored in r. If an exception is thrown, the constructor has no effect.
    Postconditions: use_­count() == r.use_­count().
    Throws: bad_­weak_­ptr when r.expired().


Why? Because that's the the semantics that the standard authors decided to give it. If you attempt to construct a std::shared_ptr directly from a std::weak_ptr then you are asserting that the weak_ptr is valid. If you're not sure, use lock.

  • Related