Channels ▼
RSS

C/C++

Smart Pointers in Boost


shared_array

Again, shared_array is the equivalent of shared_ptr, but it is used with arrays.

shared_array<Base> MyStrings( new Base[20] );

Diving into the shared_ptr Implementation

Creating a simple smart pointer is fairly easy. Creating a smart pointer that will work with most compilers is more difficult. Creating a smart pointer with exception safety in mind is a lot more difficult. Boost::shared_ptr does it all, and here's how. (Note: includes, broken compiler fixes, and parts of the implementation are omitted, but you'll find them in Boost.smart_ptr).

First, the class definition: obviously, smart pointers are (almost always) templates.

template<typename T> class shared_ptr {

The public interface:

explicit shared_ptr(T* p =0) : px(p) {
  // fix: prevent leak if new throws
  try { pn = new long(1); }  
  catch (...) { checked_delete(p); throw; }
}

Now, in the constructor there are two things that are easily forgotten. The constructor is explicit, just like most constructors that can take a single argument. The other important thing to note is that the heap allocation of the reference counter is protected by a try-catch block. Without it, you would have a flawed smart pointer that wouldn't do its only job if the reference counter can't be allocated.

  ~shared_ptr() { dispose(); }

The destructor has another important task: if the reference count goes down to zero, it should safely delete the pointee. The destructor delegates this task to another method: dispose.

void dispose() { if (—*pn == 0)
{ checked_delete(px); delete pn; } }

As you can see, the reference count (pn) is decremented. If it comes down to zero, checked_delete is called on the pointee (px), and then the reference count (pn) is also deleted.

So, what does checked_delete do? This handy function (which you'll find in Boost.utility) makes sure that the pointer represents a complete type. Do you have that in your own smart pointer classes?

Now, here's the first assignment operator:

template<typename Y> shared_ptr& operator=
  (const shared_ptr<Y>& r) {
  share(r.px,r.pn);
  return *this;
}

This is a member template, and if it had not been, there are two scenarios:

  1. If there is no parameterized copy constructor, assignment of the type Base = Derived won't work.
  2. If there is a parameterized copy constructor, assignments will work, but it involves creating an unnecessary temporary smart_ptr.

Again, this shows you a very good reason why you shouldn't roll your own smart pointers — these are not obvious issues.

The actual work of this assignment operator is done by share:

void share(T* rpx, long* rpn) {
  if (pn != rpn) { // Q: why not px != rpx?
                   // A: fails when both == 0
    ++*rpn; // done before dispose() in case
            // rpn transitively dependent on
            // *this (bug reported by Ken Johnson)
    dispose();
    px = rpx;
    pn = rpn;
  }
}

Note that the comparison for self assignment (or rather self sharing) is done by comparing the reference counters, not the pointers. Why? They both can be zero, but not necessarily the same.

template<typename Y> shared_ptr
  (const shared_ptr<Y>& r) : px(r.px) {  // never throws
    ++*(pn = r.pn);
  }

This version is a templated copy constructor. See above for a good reason why.

The assignment operator and copy constructor also have non-templated versions:

shared_ptr(const shared_ptr& r) :
// never throws
  px(r.px) { ++*(pn = r.pn); }
shared_ptr& operator=
  (const shared_ptr& r) {
  share(r.px,r.pn);
  return *this;
}

The reset function is true to its name by resetting the pointee. Very handy if you need to destroy a pointee before leaving scope, or simply to invalidate some cached values.

  void reset(T* p=0) {
    // fix: self-assignment safe
    if ( px == p ) return;  
    if (—*pn == 0) 
    { checked_delete(px); }
    else { // allocate new reference 
           // counter
      // fix: prevent leak if new throws
      try { pn = new long; }  
      catch (...) {
        // undo effect of —*pn above to
        // meet effects guarantee
        ++*pn;  
        checked_delete(p);
        throw;
      } // catch
    } // allocate new reference counter
    *pn = 1;
    px = p;
  } // reset

Again, note the care that has been taken to avoid potential leaks and to maintain exception safety.

Then you have the operators that enable the "smart" of the smart pointer:

// never throws
T& operator*() const   { return *px; }
// never throws
T* operator->() const  { return px; }
// never throws
T* get() const         { return px; }  

Just a note: there are smart pointers that implement a cast operator to T*. This is not a good idea, and more often than not, you'll burn your fingers. The inconvenience of having to write get disables the compiler from playing games with your sanity.

I think it was Andrei Alexandrescu who said "If your smart pointer behaves exactly like dumb pointers, they are dumb pointers." True enough.

In closing, here are a couple of convenient functions.

long use_count() const  
{ return *pn; }  // never throws
bool unique() const     
{ return *pn == 1; }  // never throws

The names are self-explanatory, right?

There is a lot more to mention about Boost.smart_ptr (such as specializations for std::swap and std::less, members for compatibility and convenience together with std::auto_ptr, etc.), but I'm running out of space. See smart_ptr.hpp in the Boost distribution (<www.boost.org>) for the gory details. Even without those extras, isn't this indeed a very smart pointer?

Conclusion

This has been a short introduction to the world of Boost. Anyone is welcome to join, and frankly, I think that most C++ programmers have very good reasons to do so. I'd like to thank Beman Dawes, David Abrahams, and Jens Maurer for their help in answering questions and sharing their opinions (see sidebar, "Answers from the Original Boosters") on the next page.

See you at Boost!

Notes and References

[1] Andrei Alexandrescu. Modern C++ Design (Addison-Wesley, 2001).

[2] Boost, www.boost.org.

[3] Jaako Jarvi. Tuple Types and Multiple Return Values, C/C++ Users Journal, August 2001.

[4] Jim Hyslop and Herb Sutter.I'd Hold Anything for You,C/C++ Users Journal C++ Experts Forum, December 2001.

[5] Boost mailing list, http://groups.yahoo.com/group/Boost.

[6] Boost-Users mailing list, http://groups.yahoo.com/group/Boost-Users

[7] C++ Standard, International Standard ISO/IEC 14882.


Bjorn Karlsson is a professional software developer at ReadSoft. Bjorn can be reached at bjorn.karlsson@readsoft.com.


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.
 

Video