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_ptrin 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_ptrobject that shares ownership withrand 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_ptrwhenr.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.
