Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

The New C++:Smart(er) Pointers


Last time [1], I gave a survey of the first batch of suggested library extensions that were considered at the October 2001 C++ standards meeting in Redmond, Washington, USA. This time, as promised, we'll take a closer look at one of the proposed facilities — smart pointers, which were discussed again at the following April 2002 standards meeting in Curaçao, in the Netherlands Antilles.

And Then There Was One

In today's Standard C++, there's only one smart pointer: std::auto_ptr.

Considering that it's such a tiny feature, auto_ptr sure has received copious attention. The main reason for the attention is that attention is deserved, both positively and negatively. On the one hand, auto_ptr is very useful for specific situations and those uses deserve to be described; see the discussion in Items 19 and 37 of Exceptional C++ [2], Item 10 of More Effective C++ [3], and my article "Using auto_ptr Effectively," available online [4]. On the other hand, auto_ptr can also be moderately suspicious to highly dangerous in other situations, such as the minefield of trying to put auto_ptrs into standard containers like vector and map; see for example Item 21 of More Exceptional C++ [5] and Item 28 of More Effective C++ [3]. (On the gripping hand, as Niven and Pournelle might say, auto_ptr relies on some fairly obscure language trickery to make it deliberately not work in some dangerous cases. Fortunately this trickery gets less press — the details are not for the faint of heart. Suffice it to say that auto_ptr is designed with some sleight of hand that's intended to make it deliberately break when used with, say, standard containers.)

So today's Standard C++ has exactly one smart pointer: auto_ptr. That's it. So it must be all you need, right? Not a bit of it. There's more to this story.

It's actually a real shame that auto_ptr is the only standard smart pointer, for at least two reasons. First, auto_ptr doesn't do all the useful things that smart pointers might be good for; there are many good uses of smart pointers that poor auto_ptr was never designed to support, and shouldn't, and doesn't. Second, auto_ptr can be a dangerous snake that turns and bites you on the hand if you do try to use it in some of those other ways.

The first piece of good news is that many good and useful smart pointers are available and were available even before auto_ptr. The only problem is that they weren't, and aren't, standard. That's somewhere between disappointing and annoying, depending on how portable your code needs to be — standard features are of course generally the most portable.

The second piece of good news is that those alternative smart pointers are now strong candidates for inclusion into the Standard. (Some could have been in the original Standard; see [1]. But better late than never.)

If One Is Good, Six Are Better... (?)

The Boost library ships with five additional smart pointers [6]. All of these smart pointer templates take a single type parameter specifying the type of the object to be held. Briefly, here they are:

  • scoped_ptr: a non-copyable smart pointer intended to be used as an auto (stack) object. When a scoped_ptr goes out of scope and is destroyed, it will automatically delete the single object it points to. Arguably, scoped_ptr is what auto_ptr ought to have been in the first place, way back when auto_ptr was first meant to be really an "auto" pointer. But scoped_ptr doesn't support the modern auto_ptr's important additional usefulness for sources and sinks (described in [4]).
  • scoped_array: like scoped_ptr, but owns an array instead of a single object.
  • shared_ptr: a reference-counted smart pointer intended to be used to share "handles" to the pointed-at object. When a shared_ptr goes out of scope and is destroyed, if it is the last shared_ptr pointing at the owned object, it will automatically delete the owned object — a classic case of "last one out, turn off the lights." Note that shared_ptr does support auto_ptr's important use for sources and sinks. More importantly, it can be safely used in a container, which for auto_ptrs is not allowed and highly dangerous.
  • shared_array: like shared_ptr, but owns an array instead of a single object.
  • weak_ptr: to be used in conjunction with a shared_ptr. If you have an object that's already managed by one or more shared_ptrs, you can create weak_ptrs to it too. Now, the shared_ptrs still follow the "last one out, turn off the lights" policy; the last shared_ptr will delete the owned object even if there are still weak_ptrs to it. How is weak_ptr then any better than just a bald pointer to the object? Because the shared_ptr that turns off the lights will also check to see if any weak_ptrs still point to that object and set them to null if they do. That's a level of safety you don't get with plain old raw pointers.

Let me repeat some advice from last time, because it's worth repeating: if you know nothing else about Boost, know about shared_ptr. It's especially valuable if you ever want to have a container of pointers, because what you almost always really want is a container of shared_ptrs. For one thing, they'll be managed for you and will get cleaned up correctly, and they'll avoid the usual pitfalls of using mutating STL algorithms on containers of bald pointers. For another, they are not auto_ptrs, which is a Good Thing because auto_ptrs should never be put into STL containers.

The Boost approach is to have lots of special-purpose smart pointer types, one for each kind of smart pointer behavior. In this model, a smart pointer template is quite simple:

template<typename T> class scoped_ptr;
template<typename T> class scoped_array;
template<typename T> class shared_ptr;
template<typename T> class shared_array;
template<typename T> class weak_ptr;

Only one template parameter is needed, to specify the type of the owned object. It is both an advantage and a disadvantage that these little class templates can all have very different interfaces if they want to. On the one hand, it allows customization, say when a member function might make sense for one but not another smart pointer. But, on the other hand, it also means that it's easy to get inconsistent interfaces, especially when people start extending the facility by providing more smart pointers of their own, both within and outside the Boost library.

If Six Are Good, Infinity Is Better... (?!)

Although Boost chooses to follow the model of designing special-purpose smart pointer templates, we can ask: "Why stop there?" In fact, some very bright language designers, notably Andrei Alexandrescu in Modern C++ Design [7], have proposed that a "one size fits all" über-pointer may be an achievable dream, using policy-based design.

Policy-based design takes the view that certain details, such as checking policies and ownership policies, can be abstracted out into their own classes, which are then supplied as template parameters to configure or customize the basic smart pointer. Thus Loki's SmartPtr template looks something like this:

template
<
  typename T,
  template <class> class OwnershipPolicy = RefCounted,
  class ConversionPolicy = DisallowConversion,
  template <class> class CheckingPolicy = AssertCheck,
  template <class> class StoragePolicy = DefaultSPStorage
>
class SmartPtr;

Note that we still have the obligatory template parameter T that describes the type of the pointed-at object. But we also get policies that govern much of the behavior of the smart pointer. The policies may look cumbersome to type out, but they are defaulted for those who don't want or need to specify them; the default Loki::SmartPtr is a lot like a boost::shared_ptr. In fact, SmartPtr covers all that the four basic Boost smart pointers can do, and more. (An equivalent of Boost's weak_ptr could also be provided to work with a partial specialization of SmartPtr corresponding to shared_ptr.)

Indeed, Loki's SmartPtr is so much a superset of Boost's smart pointers that we would love to just create synonyms. In Standard C++, the following code won't work because it relies on a feature that was frankly just overlooked, namely typedef templates (for some existing discussion, see [8]; I'll be writing more about this up-and-coming feature in the future). Barring major surprises, such as the Earth suddenly deciding to break out of its usual orbit and head for Mars on a holiday, typedef templates will be in the next C++ Standard, C++0x (see [9]). If we had this feature, we could write the following synonym:

namespace boost {
  template<typename T>    // typedef templates aren't standard
  typedef Loki::SmartPtr  // yet, but let's overlook that for now...
    <
      T,                  // note, T still varies
      RefCounted,         // but everything else is fixed
      DisallowConversion,
      NoCheck,
      DefaultSPStorage
    >

    shared_ptr;
}

So that the usage would simply be, as usual:

shared_ptr<int> a;        // simple -- no fuss, no muss!

Similar synonyms could be created for shared_array, scoped_ptr, scoped_array, and even std::auto_ptr. Such simple names for common configurations would make all the tedious template parameters go away for those common cases. We can't do that today, alas, but once typedef templates become commonly available we will be able to do this and more.

SmartPtr really then describes an unlimited family of smart pointers having a consistent interface. Note that because Loki's SmartPtr inherits publicly from its template parameter types, the interface does not have to be identical across the whole family; the policies could add some visible functionality too. Loki already comes with several predefined policies allowing hundreds of combinations. But you can add your own policies, such as for managing COM and CORBA distributed objects and other advances uses, so the size of the SmartPtr family really is effectively unlimited.

Summary

The standards committee is still considering these options and welcomes other smart pointer submissions. In particular, there is ongoing discussion, with different points of view, about whether the "one size fits all" customizable policy-based design is the way to go, or whether just a few special-purpose smart pointer templates are needed. Want to weigh in? Start discussion on the Boost reflector [10] or on the comp.std.c++ newsgroup.

Next time: more news from the April 2002 standards meeting in Curaçao. Stay tuned.

References

[1] H. Sutter. "The New C++: The Group of Seven — Extensions under Consideration for the C++ Standard Library," C/C++ Users Journal C++ Experts Forum, April 2002, <www.cuj.com/experts/2004/sutter.htm>.

[2] H. Sutter. Exceptional C++ (Addison-Wesley, 2000).

[3] S. Meyers. More Effective C++ (Addison-Wesley, 1996).

[4] H. Sutter. "Using auto_ptr Effectively," C/C++ Users Journal, October 1999, available online at <www.gotw.ca/publications/using_auto_ptr_effectively.htm>.

[5] H. Sutter. More Exceptional C++ (Addison-Wesley, 2002).

[6] <www.boost.org/libs/smart_ptr/index.htm>

[7] A. Alexandrescu. Modern C++ Design (Addison-Wesley, 2001).

[8] H. Sutter. "Guru of the Week #79: Template Typedef," available online at <www.gotw.ca/gotw/079.htm>.

[9] H. Sutter. "The New C++," C/C++ Users Journal C++ Experts Forum, February 2002, <www.cuj.com/experts/2002/sutter.htm>.

[10] C++ Boost, <www.boost.org>.

About the Author

Herb Sutter (www.gotw.ca) is secretary of the ISO/ANSI C++ standards committee, author of the acclaimed books Exceptional C++ and More Exceptional C++, and one of the instructors of The C++ Seminar (www.gotw.ca/cpp_seminar). In addition to his independent writing and consulting, he is also C++ community liaison for Microsoft.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.