1/16/2011

Tips for Smart Pointers in C++

Part I - Brief Summary for Various Smart Pointers

1. auto_ptr
- RAII and transfer-of-ownership semantics based, but no shared-ownership
- Managed heap object will be owned by one and only one
- Assignment/Copy Construction will transfer ownership
- Can be compiled with STL containers, but wrong semantic

2. scoped_ptr
- RAII semantic based, but no shared-ownership, nor transfer-of-ownership semantics
- Managed heap object will be owned by one and only one pointer
- Assignment/Copy Construction are forbidden
- Can't be compiled with STL containers

3. shared_ptr
- Reference count based
- Managed heap object could be owned by multiple smart pointers
- Assignment/Copy Construction will add ownership
- To avoid memory leak, don't construct temporary shared_ptr object on function call parameter
- Can't construct a shared_ptr object from this pointer (Causes double deletion) 

4. intrusive_ptr
- Basically the same as shared_ptr
- Shared ownership of objects with an embedded reference count
- Can be constructed from an arbitrary raw pointer of type T *
- Try shared_ptr first, if  it isn't obvious whether intrusive_ptr better fits your needs

5. weak_ptr
- Just reference, no ownership, no RAII, no shared-ownership, no transfer of ownership
- Linked to a shared_ptr object and known by it
- Shared_ptr will reset weak_ptr when it decides to destroy the dynamic object owned by it
- It's a safe(no need to worry the dangling reference) way to reference a dynamic object but don't own it
- A nice feature of weak_ptr is that, it can access the internal state of corresponding shared_ptr object 

6. unique_ptr
- C++0x  introduced a new scoped_ptr like pointer: unique_ptr to replace auto_ptr.
- It hide assignment operator and copy constructor
- Transfer-of-ownership can be done using std::move() explicitly

These smart points are only suitable for single dynamic object, for object array, use other smart pointers whose name ended as "_array".
 
Part II - Tips for shared_ptr

1. shared_ptr VS weak_ptr
- shared_ptr owns some heap object
- weak_ptr points some heap object 

2. Handling this Pointer

It's safe to construct a shared_ptr object from a newly created heap object since it's not managed by any other shared_ptr object yet. But when you want to pass this pointer to a function that expects a shared_ptr object, you will encounter a tricky problem because most likely, the heap object is already created and managed by other shared_ptr objects.

The problem is that, in general, you can't create a shared_ptr from an existing raw pointer - the new shared_ptr you create won't "know" about the other instances that refer to the same object and you'll get multiple-deletes.

2.1. Use enable_shared_from_this from boost library

You can derive from enable_shared_from_this and then you can use "shared_from_this()" instead of "this" to spawn a shared pointer to your own self object.

How it's implemented?
- Add a weak_ptr member to point to an existing shared_ptr object that manages this object
- When shared_ptr object get constructed from raw pointer to a this kind of object, it will properly set the weak_ptr inside that object
shared_from_this() will construct a safe shared_ptr object from the weak_ptr member
- In boost shared_ptr implementation, the "sp_enable_shared_from_this()" function will get called in shared_ptr's constructor. In this function, if the passed in dynamic object derives from enable_shared_from_this, it will set the weak_ptr member using itself.

If you adopt this method, you should be careful not creating such object on stack. Because when creating object on stack, the object is not managed by any shared_ptr, so no shared_ptr's constructor gets called and the corresponding weak_ptr member won't get set properly.

2.2 If you know that your object is long lived, you can do the following:

struct null_deleter
{
template void operator()(T *) {}
}

Then in your code, just return a shared_ptr(this, null_deleter()).


3. Handling Null Valued shared_ptr Object.

When you are using shared_ptr in your code, sometimes you need a NULL equivalent stuff to represent a pointer that didn't point anything meaningful.


Generally speaking, you have the following choices:
  • Return iterators and the end iterator if not found
  • Boost::optional
  • Silly return codes
Out of all the options boost::optional & exceptions (when there really are exceptional circumstances) are the best methods, if you are dealing with containers return an iterator to end and test for the end iterator.

Returning Zero/Null for smart pointers is acceptable in some cases too, when the other alternatives don't make sense. Consider the following code:

class some_class_name{
public:  
template<typename T> operator shared_ptr<T>() { return shared_ptr<T>(); }
} nullPtr;

Use this template function when any boost::shared_ptr<> typed null pointer is needed.

[Reference]

smart pointers overview
http://en.wikipedia.org/wiki/Smart_pointer
http://www.informit.com/articles/article.aspx?p=25264
http://www.drdobbs.com/184401507
http://dlugosz.com/Repertoire/refman/Classics/Smart%20Pointers%20Overview.html
http://ootips.org/yonat/4dev/smart-pointers.htm

unique_ptr
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=401

shared_ptr
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=239

weak_ptr
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=300
http://www.drdobbs.com/184402026;jsessionid=X2T3WUC5FRMSDQE1GHPSKHWATMY32JVN

shared_ptr for this pointer
http://stackoverflow.com/questions/142391/getting-a-boostshared-ptr-for-this

No comments: