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:
- In the function print_shared2, why are the semantics of wp.lock() not used during the construction of sp?
- 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:
- There is the constructor
std::shared_ptr<Y>::shared_ptr(const std::weak_ptr<Y>& r) (11)
that allows doing it. - 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, whilestd::weak_ptr<T>::lock()
constructs an emptystd::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 withT*
.
Effects: Constructs ashared_ptr
object that shares ownership withr
and stores a copy of the pointer stored inr
. If an exception is thrown, the constructor has no effect.
Postconditions:use_count() == r.use_count()
.
Throws:bad_weak_ptr
whenr.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
.