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.
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.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
.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.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.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.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());
.&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.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.1 I’m joking, there is no God. I just man the fuck up and take care of it.