7

I was reading "The C++ Standard Library" book by Nicolai M. Josuttis for understanding weak pointers. The author has mentioned 2 reasons for the need to have a weak_ptr and I don't get the second reason. Can anyone provide a simple explanation along with example of the below reason (quoted from the book):

Another example occurs when you explicitly want to share but not own an object. Thus, you have the semantics that the lifetime of a reference to an object outlives the object it refers to. Here, shared_ptrs would never release the object, and ordinary pointers might not notice that the object they refer to is not valid anymore, which introduces the risk of accessing released data.

Martin Ba
  • 37,187
  • 33
  • 183
  • 337
Tushar Jadhav
  • 335
  • 4
  • 14
  • 4
    Imagine a cache of objects that were created with shared ownership. You don't want to keep the objects alive unnecessarily, but as long as they are in fact still alive, the cache gives you a way to get a handle on the objects. – Kerrek SB Apr 25 '16 at 23:38
  • This [link](http://stackoverflow.com/questions/12030650/when-is-stdweak-ptr-useful) could help you – al0011 Apr 25 '16 at 23:46
  • Actually, maybe it would help to know what the _first_ reason the book gives for weak pointers is - I suspect it is a cache, but maybe it's not. – davidbak Apr 25 '16 at 23:53
  • The first reason is fairly straightforward. (Again quoting from the book): "If two objects refer to each other using shared_ptrs, and you want to release the objects and their associated resource if no other references to these objects exists, shared_ptr won't release the data, because the use_count() of each object is still 1. You might want to use ordinary pointers in this situation, but doing so requires explicitly caring for and managing the release of associated resources" – Tushar Jadhav Apr 25 '16 at 23:57
  • OK, the first reason is to break cycles. Though frequently you don't need them in that case either, since one of the objects in the cycle has a lifespan that controls the other. (The answer below on event sources/sinks discusses this, in the comments.) This example is for the cache use case, among others, as @KerrekSB points out. – davidbak Apr 26 '16 at 04:32

3 Answers3

10

The second half of that statement should be clear: if a pointer is not an owning pointer then the object it is pointing at might be deleted by whatever software is the owner - and then you'd have the standard dangling reference.

So this issue is: you've got objects owned by some piece of software which is letting other software have access to it - but the other software won't share the ownership. So the owner can delete it at any time and the other software needs to know its pointer is no longer valid.

Maybe an example would help:

You've got some piece of software watching a camera pointing out your window to a bird feeder and it is identifying birds at the feeder, which come and go. Each bird at the feeder has an object created by this software when it arrives at the feeder, and the object is deleted when the bird flies away.

Meanwhile, some other software it taking a census. Every 10 seconds it grabs from the feeder-watching software a collection of the birds at the feeder. Every 100 seconds it emits a report of which birds were at the feeder for the entire 100 seconds.

Because the data for a bird is big the census-taker doesn't copy the data. It merely gets, every 10 seconds, a collection of pointers from the feeder-watcher.

To make it necessary to use weak pointers, let's say the feeder-watcher only provides pointers to birds which have arrived in the last ten seconds, not the ones which have been there. That is, there is no notification that birds have disappeared.

By using weak pointers it can know, at report time, which of the birds are still there, and when they arrived (but not when they left).

(Maybe I'll think of a better example later.)

donturner
  • 17,867
  • 8
  • 59
  • 81
davidbak
  • 5,775
  • 3
  • 34
  • 50
7

e.g.:

struct node
{
   std::shared_ptr<node> left_child;
   std::shared_ptr<node> right_child;
   std::weak_ptr<node> parent;
   foo data;   
};

In this example, deleting the node will erase the left_child and right_child, but not the parent. If for some reason the node sticks around longer than the parent, and the parent is deleted, you have a way of knowing that the parent is no longer valid. (assuming you don't reference left_child or right_child with another shared_ptr)

Exaeta
  • 300
  • 1
  • 4
  • 1
    Good example, though regarding *"deleting the node will erase the `left_child` and `right_child`"* - that would be true for `unique_ptr`; for `shared_ptr`s, it *might* erase them. – Tony Delroy Apr 26 '16 at 00:17
  • 1
    Well yes, it's true assuming that you have only one shared pointer to the thing. But unique_ptr doesn't support weak_ptr. – Exaeta Apr 26 '16 at 00:33
4

imagine a callback function to an imaginary timer event source

struct my_thing : std::enable_shared_from_this<my_thing>
{
  void start()
  {
    auto weak_self = std::weak_ptr<my_thing>(shared_from_this());
    _timer.set_callback([weak_self] {
      if (auto self = weak_self.lock()) {
        self->respond_to_timer();
      }
    });
  }

  void respond_to_timer()
  {
    // do something here
  }

  SomeTimer _timer;
};

In the above example, my_thing owns the timer, but the timer has been given a callback which refers to the my_thing. This would be a circular reference which would prevent my_thing from ever being deleted.

The use of the weak_self weak_ptr breaks the cyclic ownership problem.

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • If `my_thing` actually owns the timer, why not just use a plain pointer for the callback? – Kerrek SB Apr 25 '16 at 23:55
  • 3
    @KerrekSB because `my_thing` may have been deleted while the event was in the timer's dispatcher. The action of locking the weak_ptr proves that the delegate object exists and keeps it alive during the callback. This is particularly important in a multi-threaded solution. – Richard Hodges Apr 25 '16 at 23:57
  • If `my_thing` was deleted, then so was the contained `_timer`, wasn't it? – Kerrek SB Apr 26 '16 at 00:15
  • @KerrekSB: would there not be a race condition? The `SomeTimer` data member will be "destructed" sometime after `~my_thing`'s body runs, but calling the timer after `~my_thing` starts running is undesirable. – Tony Delroy Apr 26 '16 at 00:21
  • @KerrekSB very often the event sources are decoupled from the event dispatching mechanism. The event survives the destruction of its intended delegate. You will encounter this in any system where event consumers have shorter lives that the event dispatcher (or bus). – Richard Hodges Apr 26 '16 at 00:26