std::tr1::shared_ptr tutorial

The C++ TR1 additions contain an extremely useful smart pointer: std::tr1::shared_ptr. Used properly, it allows creating efficient and safe C++ code without the risk of memory leaks. Point in case: I have a personal project with 10k + LoC without a single delete call.

How to get it, and what it is

std::tr1::shared_ptr is part of the TR1 additions to the C++ STL. With GCC, it is available either through #include <tr1/memory> (GCC 4.1) or #include <memory> (GCC 4.3), the latter form works also on Windows (with VS2008 SP1). If you want to be portable, try Boost, which uses the vendor-provided solution if possible and falls back to a custom one if no vendor solution was found. To use this, just include <boost/tr1/memory.hpp>

Basically, the shared pointer is a reference counted pointer. The references are counted using atomic instructions, so it is (mostly) thread-safe. Exception: If you pass the shared pointer by reference, and it is modified then by two threads, you'll run into problems, so copy it!

Usage examples

Let us take a look at several usage examples.

Deleting an array

An array can be deleted by providing a custom array deleter.

template <typename T>
class ArrayDeleter
{
public:
    void operator () (T* d) const
    {
        delete [] d;
    }
};

int main ()
{
    // Usage
    std::tr1::shared_ptr<double> array (new double [256], ArrayDeleter<double> ());
}

or, if you have Boost and included <boost/smart_ptr.hpp> and you need a temporary array

boost::scoped_array<double> array (new double [256]);

scoped_array is noncopyable, which makes it perfect for local buffers.

Casting

It is possible to cast shared pointers using std::tr1::dynamic_pointer_cast and std::tr1::static_pointer_cast.

class Base
{
};

class Derived : public Base
{
};

int main ()
{
    std::tr1::shared_ptr<Base> base (new Derived ());

    // Static downcast
    std::tr1::shared_ptr<Derived> derived = std::tr1::static_pointer_cast<Derived> (base);
}

Custom reference counting

If your class has custom reference counting (COM, anyone?), you can still use shared pointers for it.

class CustomReferenceCounted
{
public:
    CustomReferenceCounted (int* counter)
    : counter_ (counter)
    {
    }

    void Release ()
    {
        --(*counter_);
    }

private:
    int* counter_;
};

class CustomReferenceCountedDeleter
{
public:
    void operator () (CustomReferenceCounted* r) const
    {
        r->Release ();

        // So we don't leak memory in this example
        delete r;
    }
};

int main()
{
    int counter = 1;
    std::tr1::shared_ptr<CustomReferenceCounter> crc (new CustomReferenceCounted (&counter), CustomReferenceCountedDeleter ());
    crc.reset ();
    std::cout << counter << std::endl;
}

Usage notes

There is a slight performance hit for the shared pointer copying, so you should pass the contained object by reference (preferably) or pointer unless the called code needs a copy of the pointer (for example, when inserting into a container).

Being reference-counted, you cannot use shared pointers for cyclic data structures directly. For this, you can fall back to weak_ptr, which does not prevent deleting the pointed-to object but allows promoting to a shared pointer if needed. For a big list of more tricks, take a look at the Boost shared_ptr documentation.

[Update] Changed the link to automatically point to the latest Boost documentation.

Comments

Comments powered by Disqus