Pointing the Way Redux, Part 6: weak_ptr

shared_ptr was the latest installment in my more info then you ever wanted about which reference to use when series that has also covered values, references, bare pointers, and unique_ptr. This time I’ll be covering the last of the smart pointer trifecta: weak_ptr.

Among the smart pointers weak_ptr is sort of the odd one out since it doesn’t control the lifetime of the object referenced. It sort of sits between the level of a bare pointer and shared_ptr in that it doesn’t affect the lifetime of an object like shared_ptr does but, unlike a bare pointer, it does allow you to figure if the referenced object is still alive or not.

The canonical example of weak_ptr use is to break cycles among objects that refer to each other using shared_ptrs: object A contains a shared_ptr to object B while object B contains a shared_ptr to object A creating a shared_ptr cycle and thus a memory leak since there’s no way for the reference count for either object to get to zero. If object B instead contains a weak_ptr to object A the memory leak goes away: object A’s reference count can now go to zero and when it does the object goes away and takes object B with it. The price for this is the extra step of having to lock the weak_ptr in object B whenever you want to access object A in one of B’s methods.

That being said, I find that I rarely use weak_ptr in this fashion. I don’t know if it’s the nature of the software that I work on or the style in which I tend to design software, but it just seems very rare that I need to break a cycle as above. Instead I find myself mainly using weak_ptr as an object lifetime monitor. For example: say I have an object that I’m going to use in another thread but I only want that other thread to bother doing its thing if there is still someone around that cares about the object that I’m working with. Here I’m defining cares about the object as holds a shared_ptr to it (seems like a reasonable criteria to me). By copying a weak_ptr into the routine in the other thread I’ll know when no one cares about the object anymore by the fact that I won’t be able to lock the weak_ptr since all the shared_ptr’s to the object have been dropped. Then, instead of doing a bunch of work that no one cares about the routine can just bail out early.

As a return type I think I’ve used weak_ptr all of once, and that was the return type for make_weak, which is a special case. I can think of a theoretical need to return a weak_ptr: you’re holding a weak_ptr to an object and other objects want a weak_ptr without caring about getting a shared_ptr (probably because they just want to monitor the object’s lifetime until some later date). In this case there’s no reason to incur the overhead of locking the weak_ptr in order to return a shared_ptr since the return value will just be going straight into another weak_ptr. In practice I’ve never encountered this situation, hence the theoretical label above.

The reference count object associated with an object stored in a shared_ptr normally contains two counts: the shared and the weak counts. As you can probably imagine these are the counts of the shared_ptrs and weak_ptrs that refer to the object that is being reference counted. When the shared count goes to zero the referenced object can be deleted, but the reference count object can’t be done away with until the weak count is zero. Otherwise we’ll be leaving all the weak_ptrs for our object lying around with dangling pointers to the reference count effectively turning them into time-bombs waiting to detonate our program the next time some tries to lock one. Thus the same caveats that apply to shared_ptr apply to weak_ptr when passing them around: be wary of what’s happening to the reference count. In a way it’s worse with weak_ptr as it lacks a move constructor making all by value passing incur reference count overhead. So you will want to pass weak_ptrs as const references instead of by value the majority of the time.

The weak count is thread-safe in the same way that the shared count is, but you do need to be careful of using the same weak_ptr instance in multiple threads similar to how you have to treat shared_ptr. Also you should treat creating a weak_ptr from a shared_ptr the same as copying the shared_ptr when it comes to thread synchronization.

The only reason to hold a weak_ptr as a member variable is in the two cases given above: breaking a reference cycle or when you want to monitor an object’s lifetime but not participate in prolonging it. It’ll normally be obvious when one of those situations has arisen, sometimes less so in the cycle case. The rest of the time you’ll probably want shared_ptr instead.

So that’s it for smart pointers, at least the ones I’m interested in talking about. auto_ptr is dead, just forget about it and hope you don’t run into it in legacy code. boost::scoped_ptr isn’t quite as dead, but there’s no real reason to prefer it over unique_ptr. Should you encounter it in the wild you can probably just leave it alone and things will be fine. In the case that things aren’t fine (a need to move the object from one scoped_ptr to another has probably arisen) you should just be able to replace it with unique_ptr and move along.

Next time I’ll get into boost::optional which is sort of like a pointer but sort of not. Its use is, well, optional but it turns out to be quite useful in many cases and in those cases it has advantages over using a unique_ptr (it’s closest pointer analog).

Advertisements

2 comments

  1. […] references, bare pointers, shared_ptr and unique_ptr – I covered the last of the smart pointers: weak_ptr. This time we’ll cap off the series with boost::optional. While it’s not part of the C++ […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: