And finally,
let's discuss the last kind of a smart pointer from the standard library,
the weak pointer.
So what makes this pointer weak?
It is weak because unlike other smart pointers,
it has no ownership of resources.
We could say that it has a temporary ownership,
but weak pointers don't actually own anything; they are just
observers of resources owned by shared pointers.
I created a shared pointer and initialized it with a new complex subject.
Then I can declare a weak pointer and construct it by using that shared pointer.
Weak pointers cannot be constructed by a dynamic allocation on the heap,
because they don't own any resources.
We can either declare an empty weak pointer,
or we can initialize it with another shared or weak pointer.
In this case, I'm initializing it by using a shared pointer,
which means that now this weak pointer points to the resource,
which is owned by this shared pointer.
But we cannot de‑reference this weak pointer and access the resource,
because we don't know if the resource is still available.
Weak pointers don't increase the control block's reference count.
So this complex subject could already be released from memory when
we try to access it through the weak pointer.
When you decide to use this weak pointer to access the resource,
you first have to lock it with the lock function. Lock
function will lock this weak pointer down, and by that, I mean
it will return another shared pointer.
I used auto here, but you can see that this is a shared pointer.
Shared pointer is always the result of the lock function.
And now, there are two possibilities.
If the resource is still available,
this shared pointer will work like any other shared pointer.
It will increase the reference count in the control block for
that resource and assume ownership. Otherwise,
if the resource is not available, shared pointer will be empty.
I can check this by putting this shared pointer in a condition. In this context,
a shared pointer will be converted into a Boolean
value, and if it owns a resource, that value will be true.
However, if this pointer is empty, then the value will be false.
So we can create a condition to check if the resource
is still available, and if it is,
I want to print out both members of this complex object.
Let's compile this.
This will, of course, work because the lifetime of this
resource is tied to the first shared pointer.
But let's use the reset function on the shared pointer to manually get rid of
the complex object before the declaration of the weak pointer.
Now the weak pointer will try to lock on the
resource that doesn't exist anymore.
And if I compile this, you can see that I got our custom message.
The locked shared pointer is empty.
So as you can see, a weak pointer works kind of like an
observer, and we are not strict about having access to the
resource that it points to.
We can try to access the resource when we need it, but
other shared pointers that actually own the resource are
responsible for its management.
Weak pointers are an addition to shared pointers, and you can use them in
situations where you need to temporarily observe a resource,
for example, in implementing a cache system.
They also solve the problem of a dangling pointer, because a raw
pointer in this situation could not be sure if the resource that is
pointing to is still available or not.