Thursday, October 16, 2014

refptr

I come across this programming pattern every once in a while:

  1. In an object's constructor, allocate some data
  2. When copying the object, copy the pointer to the allocated data, rather than duplicate all of the data
  3. Delete the data in the object's destructor

Of course, this ends in disaster, because the first of these objects that gets destroyed deletes the data, leaving all of the others holding an invalid pointer.

Some types of smart pointer let you pass "ownership" of the data when copying an object by only letting one object hold the pointer at a time, but this is very limiting.  (auto_ptr)

An alternative is to have a smart pointer that holds the "master" allocation, and all others get a regular copy of the pointer.  This works, but doesn't really fix the above problem where there is no obvious master allocation.

One of the best generic solutions I have seen is to have an intermediate reference counting class that all of the objects point to, instead of having them point directly at the data.  This causes an extra level of indirection when you want to dereference a pointer, but it solves the above problem without any manager classes, ownership passing, etc.  (shared_ptr)

So, something like this:


template <typename T>
class refptrsentinel
{
 template <typename U> friend class refptr;

 refptrsentinel(T *obj) : ptr(obj), count(1) { }
 ~refptrsentinel() { delete ptr; }

 void acquire() { ++count; }
 void release() { if(--count == 0) delete this; }

 T *ptr;
 int count;
};

template <typename T>
class refptr
{
public:
 refptr() : sentinel(NULL) { }
 refptr(const refptr &rhs) { sentinel = rhs.sentinel; if(sentinel) sentinel->acquire(); }
 refptr(T *obj) { sentinel = obj ? new refptrsentinel<T>(obj) : NULL; }
 ~refptr() { if(sentinel) sentinel->release(); }

 refptr &operator =(const refptr &rhs)
 {
  if(sentinel)
   sentinel->release();
  sentinel = rhs.sentinel;
  if(sentinel)
   sentinel->acquire();
  return SELF;
 }

 operator bool() const { return sentinel != NULL; }
 bool operator !() const { return sentinel == NULL; }

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

private:
 refptrsentinel<T> *sentinel;
};

No comments:

Post a Comment