Alex Rivera | Logout

What is the best way to implement smart pointers in C++?

Asked 2009-02-02T16:33:14.677
12

I've been evaluating various smart pointer implementations (wow, there are a LOT out there) and it seems to me that most of them can be categorized into two broad classifications:

1) This category uses inheritance on the objects referenced so that they have reference counts and usually up() and down() (or their equivalents) implemented. IE, to use the smart pointer, the objects you're pointing at must inherit from some class the ref implementation provides.

2) This category uses a secondary object to hold the reference counts. For example, instead of pointing the smart pointer right at an object, it actually points at this meta data object... Who has a reference count and up() and down() implementations (and who usually provides a mechanism for the pointer to get at the actual object being pointed to, so that the smart pointer can properly implement operator ->()).

Now, 1 has the downside that it forces all of the objects you'd like to reference count to inherit from a common ancestor, and this means that you cannot use this to reference count objects that you don't have control over the source code to.

2 has the problem that since the count is stored in another object, if you ever have a situation that a pointer to an existing reference counted object is being converted into a reference, you probably have a bug (I.E., since the count is not in the actual object, there is no way for the new reference to get the count... ref to ref copy construction or assignment is fine, because they can share the count object, but if you ever have to convert from a pointer, you're totally hosed)...

Now, as I understand it, boost::shared_pointer uses mechanism 2, or something like it... That said, I can't quite make up my mind which is worse! I have only ever used mechanism 1, in production code... Does anyone have experience with both styles? Or perhaps there is another way thats better than both of these?

Edit
Report

2 Answers

3

If you stick with the ones that are in the standard library you will be fine.
Though there are a few other types than the ones you specified.

  • Shared: Where the ownership is shared between multiple objects
  • Owned: Where one object owns the object but transfer is allowed.
  • Unmovable: Where one object owns the object and it can not be transferred.

The standard library has:

  • std::auto_ptr

Boost has a couple more than have been adapted by tr1 (next version of the standard)

  • std::tr1::shared_ptr
  • std::tr1::weak_ptr

And those still in boost (which in relatively is a must have anyway) that hopefully make it into tr2.

  • boost::scoped_ptr
  • boost::scoped_array
  • boost::shared_array
  • boost::intrusive_ptr

See: Smart Pointers: Or who owns you baby?

answered 2009-02-02T17:03:47.423
1

Our project uses smart pointers extensively. In the beginning there was uncertainty about which pointer to use, and so one of the main authors chose an intrusive pointer in his module and the other a non-intrusive version.

In general, the differences between the two pointer types were not significant. The only exception being that early versions of our non-intrusive pointer implicitly converted from a raw pointer and this can easily lead to memory problems if the pointers are used incorrectly:

void doSomething (NIPtr<int> const &);

void foo () {
  NIPtr<int> i = new int;
  int & j = *i;
  doSomething (&j);          // Ooops - owned by two pointers! :(
}

A while ago, some refactoring resulted in some parts of the code being merged, and so a choice had to be made about which pointer type to use. The non-intrusive pointer now had the converting constructor declared as explicit and so it was decided to go with the intrusive pointer to save on the amount of code change that was required.

To our great surprise one thing we did notice was that we had an immediate performance improvement by using the intrusive pointer. We did not put much research into this, and just assumed that the difference was the cost of maintaining the count object. It is possible that other implementations of non-intrusive shared pointer have solved this problem by now.

answered 2009-02-02T18:16:58.743

Your Answer