Sign up ×

I've been thinking about using shared pointers, and I know how to implement one myself--Don't want to do it, so I'm trying std::tr1::shared_ptr,and I have couple of questions...

How is the reference counting implemented? Does it use a doubly linked list? (Btw, I've already googled, but I can't find anything reliable.)

Are there any pitfalls for using the std::tr1::shared_ptr?

share|improve this question
“Does it use a doubly linked list?” – For what? How would this help in reference counting? –  Konrad Rudolph Feb 8 '12 at 20:14
How would a linked list help? Also I thought you said you knew how to implement one yourself. How shared_ptr does reference counting is implementation-defined so there's no answer to this question. You can always look at the source for your implementation. –  Seth Carnegie Feb 8 '12 at 20:14
First: there is source code. Second: there is C++0x/11 so why TR1? Third: SO is-a awesome community. C++ has-a awesome community. And GCC wish they had it :) (half joking) –  sehe Feb 8 '12 at 20:15
STL made a whole episode about that. It's non-trivial, I'd say, though that's mainly on account of the atomic reference update and high level of abstraction. –  Kerrek SB Feb 8 '12 at 20:17
@KonradRudolph You can implement a reference counted pointer using a linked list. Instead of storing the number of reference centrally store a linked list of all of the current pointers. When there are none left in that list you know to delete the object. I believe that it may have certain advantages in multithreaded code as you can write a possibly lockless version. However I've never seen it actually done so I guess its not better in practice. –  JBB May 2 '13 at 9:44

3 Answers 3

up vote 16 down vote accepted

shared_ptr must manage a reference counter and the carrying of a deleter functor that is deduced by the type of the object given at initialization.

The shared_ptr class typically hosts two members: a T* (that is returned by operator-> and dereferenced in operator*) and a aux* where aux is a inner abstract class that contains:

  • a counter (incremented / decremented upon copy-assign / destroy)
  • what needed to make increment / decrement atomic (not needed is specific platform atomic INC/DEC are available)
  • an abstract virtual destroy()=0;
  • a virtual destructor.

such aux class (the actual name depends on the implementation) is derived by a family of templatized classes (parametrized on the type given by the explicit constructor, say U derived from T), that add: - a pointer to the object (same as T*, but with the actual type: this is needed to properly manage all the cases of T being a base for whatever U having multiple T in the derivation hierarchy) - a copy of the deletor object given as deletion policy to the explicit constructor (or the default deletor just doing delete p where p is the U* above) - the override of the destroy method, calling the deleter functor.

A simplified sketch can be this one:

template<class T>
class shared_ptr
    struct aux
        unsigned count;

        aux() :count(1) {}
        virtual void destroy()=0;
        virtual ~aux() {} //must be polymorphic

    template<class U, class Deleter>
    struct auximpl: public aux
        U* p;
        Deleter d;

        anximpl(U* pu, Deleter x) :p(pu), d(x) {}
        virtual void destroy() { d(p); } 

    template<class U>
    struct default_deleter
        void operator()(U* p) const { delete p; }

    aux* pa;
    T* pt;

    void inc() { if(pa) interloked_inc(pa->count); }

    void dec() 
        if(pa && !interlocked_dec(pa->count)) 
        {  pa->destroy(); delete pa; }


    shared_ptr() :pa(), pt() {}

    template<class U, class Deleter>
    shared_ptr(U* pu, Deleter d) :pa(new auximpl<U,Deleter>(pu,d)), pt(pu) {}

    template<class U>
    explicit shared_ptr(U* pu) :pa(new auximpl<U,default_deleter<U> >(pu,default_deleter<U>())), pt(pu) {}

    shared_ptr(const shared_ptr& s) :pa(, pt( { inc(); }

    template<class U>
    shared_ptr(const shared_ptr<U>& s) :pa(, pt( { inc(); }

    ~shared_ptr() { dec(); }

    shared_ptr& operator=(const shared_ptr& s)
            pa =;;
        return *this;

    T* operator->() const { return p; }
    T& operator*() const { return *p; }

Where weak_ptr interoperability is required a second counter (weak_count) is required in aux (will be incremented / decremented by weak_ptr), and delete pa must happen only when both the counters reach zero.

share|improve this answer
Shouldn't delete pa happen if the count of strong references reaches zero, regardless of weak_count? –  iFreilicht Jun 12 '14 at 23:27
pa is the pointer to the structure that contains the counters and the deleter. When the strong count gets to zero you have to delete the object, but the counters have to stay until also all weaks are gone: the zeroed strong counter is what makes the weaks to know they point to a destroyed object, and the weak counter is the number of weak that still need to know that. –  Emilio Garavaglia Jun 14 '14 at 9:45

If you want to see all the gory details, you can have a look at the boost shared_ptr implementation:

The reference counting seems to usually be implemented with a counter and platform specific atomic increment/decrement instructions or explicit locking with a mutex (see the atomic_count_*.hpp files in the detail namespace).

share|improve this answer


Yes ... If you create cycles in your shared memory pointers, then the memory being managed by the smart pointer will not be recycled when the last pointer goes out of scope because there are still references to the pointer (i.e., the cycles cause the reference count to not go down to zero).

For instance:

struct A
    std::shared_ptr<A> ptr;

std::shared_ptr<A> shrd_ptr_1 = std::make_shared(A());
std::shared_ptr<B> shrd_ptr_2 = std::make_shared(A());
shrd_ptr_1->ptr = shrd_ptr_2;
shrd_ptr_2->ptr = shrd_ptr_1;

Now, even if shrd_ptr_1 and shrd_ptr_2 go out of scope, the memory they are managing is not reclaimed because the ptr member of each are pointing to each other. While this is a very naive example of such a memory cycle, it can, if you use these types of pointers without any discipline, occur in a much more nefarious and hard-to-track fashion. For instance, I could see where trying to implement a circular linked-list where each next pointer is a std::shared_ptr, if you're not too careful, could result in problems.

share|improve this answer
Not really a pitfall if you use weak_ptr appropriately though... More of something you need to be aware of. –  Matt May 19 '14 at 20:42
@Matt weak_ptr is not a fix for circular dependencies. The only fix to bad design is changing the design, not using other tools. You can't just take one owning smart ptr (or "strong reference"), replace it with a non owning smart ptr (or "weak reference") and expect the program to work. –  curiousguy Jul 19 at 7:48
@curiousguy What I meant was, you can fix circular dependencies by using weak_ptr. So if you had a dependency where A holds a shared_ptr to B and B holds a shared_ptr to A, you could instead make B hold a weak_ptr to A, if B really shouldn't be owning A but needs to reference it safely. But this is in fact "changing the design" so yes, you can't just blindly do this and expect to fix everything. My point is, it's not a pitfall of shared_ptr and shared_ptr is actually nice because it has weak_ptr as a tool to avoid the circular dependencies. –  Matt Jul 21 at 19:11
@Matt Yes; but weak_ptr is very rarely useful (and very useful then). It is useful f.ex. when you want to keep a cache where is acceptable for items to exist in a dead state; or a in game, where a monster can exist in a list in a "not there anymore" state, etc. However, it is clearly not useful in the usual circular dependency case where you have strong "owning" links going both ways as in graph, because these links by design cannot exist in a dead state. The suggestion that "weak" ref is an easy fix for circular dependency issues is an abomination. –  curiousguy Jul 22 at 3:10

Your Answer


