diff options
-rw-r--r-- | src/base/refcnt.hh | 125 |
1 files changed, 116 insertions, 9 deletions
diff --git a/src/base/refcnt.hh b/src/base/refcnt.hh index df15294d1..baf08c62e 100644 --- a/src/base/refcnt.hh +++ b/src/base/refcnt.hh @@ -31,9 +31,24 @@ #ifndef __BASE_REFCNT_HH__ #define __BASE_REFCNT_HH__ +/** + * @file base/refcnt.hh + * + * Classes for managing reference counted objects. + */ + +/** + * Derive from RefCounted if you want to enable reference counting of + * this class. If you want to use automatic reference counting, you + * should use RefCountingPtr<T> instead of regular pointers. + */ class RefCounted { private: + // The reference count is mutable because one may want to + // reference count a const pointer. This really is OK because + // const is about logical constness of the object not really about + // strictly disallowing an object to change. mutable int count; private: @@ -44,78 +59,170 @@ class RefCounted RefCounted &operator=(const RefCounted &); public: + /** + * We initialize the reference count to zero and the first object + * to take ownership of it must increment it to one. + * + * @attention A memory leak will occur if you never assign a newly + * constructed object to a reference counting pointer. + */ RefCounted() : count(0) {} + + /** + * We make the destructor virtual because we're likely to have + * virtual functions on reference counted objects. + * + * @todo Even if this were true, does it matter? Shouldn't the + * derived class indicate this? This only matters if we would + * ever choose to delete a "RefCounted *" which I doubt we'd ever + * do. We don't ever delete a "void *". + */ virtual ~RefCounted() {} + /// Increment the reference count void incref() { ++count; } + + /// Decrement the reference count and destroy the object if all + /// references are gone. void decref() { if (--count <= 0) delete this; } }; +/** + * If you want a reference counting pointer to a mutable object, + * create it like this: + * @code + * typedef RefCountingPtr<Foo> FooPtr; + * @endcode + * + * @attention Do not use "const FooPtr" + * To create a reference counting pointer to a const object, use this: + * @code + * typedef RefCountingPtr<const Foo> ConstFooPtr; + * @endcode + * + * These two usages are analogous to iterator and const_iterator in the stl. + */ template <class T> class RefCountingPtr { protected: + /// The stored pointer. + /// Arguably this should be private. T *data; - void copy(T *d) + /** + * Copy a new pointer value and increment the reference count if + * it is a valid pointer. Note, this does not delete the + * reference any existing object. + * @param d Pointer to store. + */ + void + copy(T *d) { data = d; if (data) data->incref(); } - void del() + + /** + * Delete the reference to any existing object if it is non NULL. + * @attention this doesn't clear the pointer value, so a double + * decref could happen if not careful. + */ + void + del() { if (data) data->decref(); } - void set(T *d) - { - if (data == d) - return; - del(); - copy(d); + /** + * Drop the old reference and change it to something new. + */ + void + set(T *d) + { + // Need to check if we're actually changing because otherwise + // we could delete the last reference before adding the new + // reference. + if (data != d) { + del(); + copy(d); + } } - public: + /// Create an empty reference counting pointer. RefCountingPtr() : data(0) {} + + /// Create a new reference counting pointer to some object + /// (probably something newly created). Adds a reference. RefCountingPtr(T *data) { copy(data); } + + /// Create a new reference counting pointer by copying another + /// one. Adds a reference. RefCountingPtr(const RefCountingPtr &r) { copy(r.data); } + + /// Destroy the pointer and any reference it may hold. ~RefCountingPtr() { del(); } + // The following pointer access functions are const because they + // don't actually change the pointer, though the user could change + // what is pointed to. This is analagous to a "Foo * const". + + /// Access a member variable. T *operator->() const { return data; } + + /// Dereference the pointer. T &operator*() const { return *data; } + + /// Directly access the pointer itself without taking a reference. T *get() const { return data; } + /// Assign a new value to the pointer const RefCountingPtr &operator=(T *p) { set(p); return *this; } + + /// Copy the pointer from another RefCountingPtr const RefCountingPtr &operator=(const RefCountingPtr &r) { return operator=(r.data); } + /// Check if the pointer is empty bool operator!() const { return data == 0; } + + /// Check if the pointer is non-empty operator bool() const { return data != 0; } }; +/// Check for equality of two reference counting pointers. template<class T> inline bool operator==(const RefCountingPtr<T> &l, const RefCountingPtr<T> &r) { return l.get() == r.get(); } +/// Check for equality of of a reference counting pointers and a +/// regular pointer template<class T> inline bool operator==(const RefCountingPtr<T> &l, const T *r) { return l.get() == r; } +/// Check for equality of of a reference counting pointers and a +/// regular pointer template<class T> inline bool operator==(const T *l, const RefCountingPtr<T> &r) { return l == r.get(); } +/// Check for inequality of two reference counting pointers. template<class T> inline bool operator!=(const RefCountingPtr<T> &l, const RefCountingPtr<T> &r) { return l.get() != r.get(); } +/// Check for inequality of of a reference counting pointers and a +/// regular pointer template<class T> inline bool operator!=(const RefCountingPtr<T> &l, const T *r) { return l.get() != r; } +/// Check for inequality of of a reference counting pointers and a +/// regular pointer template<class T> inline bool operator!=(const T *l, const RefCountingPtr<T> &r) { return l != r.get(); } |