Sunday, February 21, 2010

Boost Pointers

scoped_ptr for automatic destruction:

Using normal pointersUsing scoped_ptr
  void Sample1_Plain() {   CSample * pSample(new CSample);    if (!pSample->Query() )   // just some function...    {     delete pSample;     return;   }    pSample->Use();   delete pSample;  }
#include "boost/smart_ptr.h"   void Sample1_ScopedPtr() {   boost::scoped_ptr<CSample>               samplePtr(new CSample);    if (!samplePtr->Query() )   // just some function...      return;           samplePtr->Use();  }


Reference counting pointers(shatrd_ptr) track how many pointers are referring to an object, and when the last pointer to an object is destroyed, it deletes the object itself, too.

The boost::shared_ptr implementation has some important features that make it stand out from other implementations:

  • shared_ptr<T> works with an incomplete type:

    When declaring or using a shared_ptr<T>, T may be an "incomplete type". E.g., you do only a forward declaration using class T;. But do not yet define how Treally looks like. Only where you dereference the pointer, the compiler needs to know "everything".

  • shared_ptr<T> works with any type:

    There are virtually no requirements towards T (such as deriving from a base class).

  • shared_ptr<T> supports a custom deleter

    So you can store objects that need a different cleanup than delete p. For more information, see the boost documentation.

  • Implicit conversion:

    If a type U * can be implicitly converted to T * (e.g., because T is base class of U), a shared_ptr<U> can also be converted to shared_ptr<T> implicitly.

  • shared_ptr is thread safe

    (This is a design choice rather than an advantage, however, it is a necessity in multithreaded programs, and the overhead is low.)

    Using shared_ptr in containers

    Many container classes, including the STL containers, require copy operations (e.g., when inserting an existing element into a list, vector, or container). However, when this copy operations are expensive (or are even unavailable), the typical solution is to use a container of pointers:

    std::vector<CMyLargeClass *> vec; vec.push_back( new CMyLargeClass("bigString") );

    However, this throws the task of memory management back to the caller. We can, however, use a shared_ptr:

    typedef boost::shared_ptr<CMyLargeClass>  CMyLargeClassPtr; std::vector<CMyLargeClassPtr> vec; vec.push_back( CMyLargeClassPtr(new CMyLargeClass("bigString")) );

    Very similar, but now, the elements get destroyed automatically when the vector is destroyed - unless, of course, there's another smart pointer still holding a reference.

    Rules : shared_ptr usage

    • When creating a smart pointer, you explicitly have to write ..._ptr<T> myPtr(new T)
    • You cannot assign a T * to a smart pointer
    • You cannot even write ptr=NULL. Use ptr.reset() for that.
    • To retrieve the raw pointer, use ptr.get(). Of course, you must not delete this pointer, or use it after the smart pointer it comes from is destroyed, reset or reassigned. Use get() only when you have to pass the pointer to a function that expects a raw pointer.
    • You cannot pass a T * to a function that expects a _ptr<T> directly. You have to construct a smart pointer explicitly, which also makes it clear that you transfer ownership of the raw pointer to the smart pointer. (See also Rule 3.)
    • There is no generic way to find the smart pointer that "holds" a given raw pointer. However, the boost: smart pointer programming techniques illustrate solutions for many common cases.
    • No circular references - If you have two objects referencing each other through a reference counting pointer, they are never deleted. boost provides weak_ptrto break such cycles (see below).


No comments:

Post a Comment