简体   繁体   中英

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

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?

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 .

  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.

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM