In the provided first example the 'observer' polls (approx.) every second to see if the observed object has expired. That's because wp.lock()
returns a shared-pointer to the object iff it has not expired (wp.expired()==false
).
The comment //The managed object is destroyed.
is not completely accurate. The object has expired and has been destroyed or some thread is about to or is in the process of destroying it - but it may not be destroyed.
So that slight inaccuracy aside the code works.
However the code proposed in the second example assumes that because the use_count()
is 1 the std::shared_ptr<int>
sp
is "the last of it's kind" and when that shared-owner is destructed on return of the lambda-function then the object shall be destroyed.
Not so in general. There may be a weak_ptr<>
referring to that object and if a call to lock()
on that object happens after the call to use_count()
(*) the use count will be increased to 2 and returning from the lambda-function (as the thread enters termination) will not cause the object to be destroyed.
The point here is that as soon as any weak_ptr<>
returns true
from expired()
the object is in train to be destroyed and no valid use can obtain another shared_ptr<>
to it.
But shared-pointers can be obtained from weak-pointers (if at least one shared-pointer remains).
See this note from C++ Reference on expired()
which is quite telling (my emphasis):
This function is inherently racy if the managed object is shared among
threads. In particular, a false result may become stale before it can
be used. A true result is reliable.
But while there is a shared_ptr<>
any weak_ptr<>::lock()
may succeed in a "lock" on the object and create a new shared_ptr<>
but once the object is expired, it shall not / cannot be (somehow) reprieved.
This is a common feature of 'reference counting' designs.
The only truly reliable count is 0 - there's "no coming back from that".
If you don't implement weak-pointers you can with effort count references and work out if you can account for all the sharing owners.
But if you do implement weak-pointers that game is up.
If you then decide to start counting weak-pointers and making decisions about destruction based on them, well they're not weak-pointers anymore and that's a different game all together.
However do notice that reasoning about 'reference count' is a one trick pony. It only takes two observer threads (from the Question) to be in a deadlock where each is waiting for the other to release their ownership to destroy the object.
Some corner of code can play that game but it's not a reliable model for managing shared ownership.
Footnote:
I strongly advise against using use_count()
for anything other than debugging because the only really meaningful return value is 0 and that is equivalent to expired()==true
anyway.
IMHO use_count()
is an uncommon error in the C++ Standard Library in which 'helper debug' members have been exposed and inevitably lead developers astray as well as nailing the class to a particular design which the Standard Library generally does not.
(*) Or is otherwise not reflected in the use count returned such as relaxed memory barriers regarding the count returned.