The USS Quad Damage

Notes on Smart Pointers

Here are some things I learned about smart pointers which no one seems to have written up yet.

The java world has far better documentation than the C++ world. There are some cases when you pile frame-works on top of each other that the java world struggles, but even basic language issues seem to be sparsely discussed. I’ve actually found myself enjoying memory management and RAII, with the exception of the Windows CComPtr. Here’s are some things I’ve learned about smart pointers, and conjectures on how to use them properly.

  1. Ye olde pointers — These aren’t smart pointers. These should basically never be used. They screw up practically everything. Pretty much every smart pointer will get confused if you stick a normal pointer in its lifecycle. Unfortunately, there will come a time when a smart pointer will need to get turned into a normal pointer. The best technique is to find a God and pray1.
  2. References — These aren’t pointers. These should be used when the receiver is explicitly not taking responsibility over the memory management of the reference. The lifecycle of an object which takes a reference should always be within the lifecycle of the reference passed in.
  3. boost::scoped_ptr — This is meant for allocating and freeing an object strictly in a scope. The “responsibility” of the object is strictly within that scope. Scoped pointers cannot be assigned, and should only be passed around as references to objects with a smaller lifecycle than the scope.
  4. std::auto_ptr — These are meant for use “like an automatic variable”, but will pass on any responsibility. These should not be used directly, rather should be used as parameters or return values. When, say, a factory is creating an object which the caller is responsible for, or you need an object as a parameter which you will clean up, use an auto_ptr.
  5. boost::shared_ptr — These imply a shared responsibility. These should be used when multiple objects may use an object over a lifetime which is unclear (or at least, not predicated on a particular scope). In a multi-threaded application, shared pointers could be used to share data objects or messages around. Shared pointers should only be assigned to other shared pointers, or perhaps references; nothing else.
  6. boost::intrusive_ptr — This lets the class know that there’s a smart pointer pointing to it. This may be necessary sometimes, but it has the stench of sulphur about it. I’ll stay away until I need to know more.
  7. CComPtr — This was probably actually forged in hell. I don’t know what possible point this thing has, only that it’s incredibly dangerous to use. A CComPtr should only be assigned to another CComPtr (but see the notes). It could also be assigned to a reference.
    • CComPtr is intrusive. Objects know about how many other objects are pointing to them. Worse, by definition the initial reference count for a new object is 1.
    • CComPtr is assignable. When you assign either a new object / pointer to a CComPtr, the reference count goes up, so CComPtr item = new Item() will result in item not getting cleaned up at the end of scope.
    • Construction semantics are like assignment semantics. That is, if you go CComPtr item(new Item()), the item will not be cleaned up at the end of the method. The correct way to assign a new object to a CComPtr is CComPtr item; item.Attach(new Item());.
    • The address operator (i.e. &item) returns a raw pointer, not the address of the CComPtr. While in this “mode”, the CComPtr will be updated with whatever’s in the raw pointer, but no reference incrementing or freeing will occur. So *(&ccp1) = ccp2 (where both ccp1 and ccp2 are CComPtrs) will result in the object being freed twice at the end of scope. Worse, this is the default way you grab objects from Windows.
    • [new] In order for normal C++ to “deal” with this crazy address of operator, there’s a CAdapt template class which you wrap your CComPtrs in. That is, if you want a std::vector of CComPtr, you need to write std::vector< CAdapt<CComPtr> >. Unbelievable.
    • [new] With smart pointers, you can forward declare classes which the smart pointer contains. This is not the case with intrusive pointers. You’ll need to include the full class.

1 I’m joking, there is no God. I just man the fuck up and take care of it.