Pointing the Way Redux, Part 2: References

Continuing on with my series going into way more detail than anyone probably wants to go into about which reference type to use when, we now arrive at part 2: references (see motivation and part 1).

Most of the time, by returning a reference to an object you’re making the statement that the object referred to will be around as long as the object returning it is. Basically you’re saying: “here, you can use this as long as I’m around”. You’re also telling the caller that you’re returning an object that can be used; unless you’re doing something wrong the reference should not be null. This doesn’t necessarily mean that the object being referenced will be valid: you could be returning a reference to a boost::optional and while the optional object is usable (e.g. you can test it to see if it contains anything) it might be empty (aka invalid).

In order to return a reference when thread synchronization is a concern there are two conditions that need to be met: the object being referenced must be internally synchronized (i.e. synchronizes its own internal state) and no other thread can come along and invalidate the reference while the reference is in use. Really the first condition is a consequence of the second: in order to be thread safe the referenced object either needs to be immutable or internally synchronized. But if the object is immutable then in order to update it you have to replace it and this would invalidate any references to the object being replaced. The second condition – no reference invalidation – precludes this so we have to have internally synchronized objects, unless the object is set in stone and never changes. In order to handle immutable objects that can be updated you either have to return by value so the caller gets a copy or use shared_ptr to const to make sure that the replaced object is still usable by anyone who already has a reference to it.

Most of the time references are what your function arguments should be. Cases where this doesn’t apply include: small objects (better performance to pass by value), ownership transfer (pass by value and move, or pass a shared_ptr or unique_ptr), the function needs its own copy to mutate without affecting the original object (pass by value), or the argument is optional (pass a bare pointer or boost::optional). Otherwise using a reference probably works best: the caller doesn’t have to check the validity of the reference, there’s no copying going on, and performance matches bare pointers; it’s hard to beat. Just be sure to make judicious use of const to let the callers know what your intentions are towards their objects.

The only time you should use references as member variables is when you can guarantee that the referenced object will outlive the object the member variable is part of. In my experience that doesn’t happen very often; normally it’s only true when the containing object has a short life on the stack so that its lifetime is easily reasoned about. If I see a reference as a member variable I tend to assume that the writer of the code did the due diligence to make the guarantee about the referenced object’s lifetime and just assume that the referenced object will be around in any context where I might be using the reference.

Next time: bare pointers (yep, they still have their uses).

Advertisements

7 comments

  1. […] detail about what type of reference to use when is bare pointers (see motivation and parts 1 and 2). Even though we now have an abundance of smart pointers to choose from – about which I’ll have […]

  2. […] far in my series on what reference types to use when we’ve seen values, references and bare pointers. The first makes a strong statement about ownership when used as a return value: […]

  3. […] far we’ve covered Values, references, bare pointers, and unique_ptr in my more info then you ever wanted about which reference to use […]

  4. […] 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 […]

  5. […] then you ever wanted about which reference to use when series – that has also covered values, references, bare pointers, shared_ptr and unique_ptr – I covered the last of the smart pointers: weak_ptr. […]

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: