http://blog.chinaunix.net/uid-25872711-id-3032607.html
Efficient C++ 第十二章 引用计数
引用计数机制的实现,和其优劣。实现方式的特点,决定它不可能在任何情况下都有性能优势。
应用编写过程中较多的使用指针的模块经常会遇到:内存泄漏、空指针(指针删除过早)的问题。为解决此类问题我们通常需要花费大量时间分析问题所在,小心的控制指针的分配和释放。
引用计数的设计思想是:把对象清除的责任从客户端代码交给对象本身,它有一个引用计数器,程序中该对象只有一份内存,拷贝它只是对计数器+1,当计数器值为0时删除自己。
实现细节
用BigInt做一个引用计数示例,BigInt把正整数表示为每一个十进制数的二进制编码。(不用关心它本身的细节)
结构关系如下图:
主要代码如下:
class BigInt { friend BigInt operator+ (const BigInt&, const BigInt&); public: BigInt (const char *); BigInt(unsigned = 0); BigInt (const BigInt&); BigInt& operator= (const BigInt&); BigInt& operator+= (const BigInt&); ~BigInt(); char *getDigits() const { return digits; } unsigned getNdigits() const { return ndigits; } private: char *digits; unsigned ndigits; unsigned size; // size of allocated string BigInt (const BigInt&, const BigInt&);// operational ctor char fetch(unsigned i) const; }; //我们只需要关心类的以下几个函数实现 BigInt::BigInt (unsigned u) { unsigned v = u; for (ndigits = 1; (v/=10) > 0; ++ndigits) { ; } digits = new char[size=ndigits]; for ( unsigned i = 0; i < ndigits; ++i) { digits[i] = u%10; u /= 10; } } BigInt::~BigInt() { delete [] digits; } BigInt& BigInt::operator= (const BigInt& rhs) { if (this == &rhs) return *this; ndigits = rhs.ndigits; if (ndigits > size) { delete [] digits; digits = new char[size = ndigits]; } for (unsigned i = 0; i < ndigits; ++i) { digits[i] = rhs.digits[i]; } return *this; }
RCObject是用于引用计数的基类,它封装了引用计数变量及其有关操作。
class RCObject { public: void addReference() { ++refCount;} void removeReference() { if (--refCount == 0) delete this;} void markUnshareable() { shareable = false;} bool isShareable() const { return shareable; } bool isShared() const { return refCount > 1; } protected: RCObject() : refCount(0), shareable(true) {} RCObject(const RCObject& rhs) : refCount(0), shareable(true) {} RCObject& operator=(const RCObject& rhs) {return *this;} virtual ~RCObject() {} private: int refCount; bool shareable; };
现在让BigInt从RCObject类继承,使其拥有引用计数特性。
class BigInt : public RCObject { // Same as before };
实现RCPtr模板类,注意->和*重载操作符的实现,它是一个智能指针:
template <class T> class RCPtr { public: RCPtr(T *realPtr = 0) : pointee(realPtr) { init();} RCPtr(const RCPtr& rhs) : pointee(rhs.pointee) { init();} ~RCPtr() { if (pointee) pointee->removeReference();} RCPtr& operator=(const RCPtr& rhs); T* operator->() const { return pointee; } T& operator*() const { return *pointee; } private: T *pointee; void init(); }; template <class T> void RCPtr<T>::init() { if (0 == pointee) return; if (false == pointee->isShareable() ) { pointee = new T(*pointee); } pointee->addReference(); } template <class T> RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs) { if (pointee != rhs.pointee) { if (pointee) pointee->removeReference(); pointee = rhs.pointee; init(); } return *this; }
最后,我们有了实现引用计数BigInt的全部代码,实现RCBigInt非常简单,复杂的工作都交给RCPtr和RCObject完成。
class RCBigInt { friend RCBigInt operator+ (const RCBigInt&, const RCBigInt&); public: RCBigInt (const char *p) : value (new BigInt(p)) {} RCBigInt (unsigned u= 0) : value (new BigInt(u)) {} RCBigInt (const BigInt& bi) : value (new BigInt(bi)) {} void print() const { value->print(); } private: RCPtr<BigInt> value; }; inline RCBigInt operator+ (const RCBigInt& left, const RCBigInt& right) { return RCBigInt(*(left.value) + *(right.value)); }
从实现逻辑上可以看出,进行复制操作时引用计数类实际上只分配了一份内存,大量的复制操作情况下其性能优势比较明显。普通的一次一次分配新对象的情况,引用计数没有性能优势,反而因逻辑复杂化而性能受损。
对已存在的类实现引用计数
上节对BigInt实现引用计数需要对BigInt做一定的修改,让它继承自RCObject。本节介绍不修改BigInt类的实现,因为很多情况下我们需要做引用计数的类是不允许我们进行修改的。
这需要引用一个独立的类CountHolder,结构如下:
http://blog.chinaunix.net/attachment/201112/14/25872711_13238324240m0X.jpg
实现一个新的智能指针RCIPtr,它和RCPtr基本相同。
class RCBigInt { ... // The same as before private: RCIPtr<BigInt> value; // RCIPtr not RCPtr }; template < class T> class RCIPtr { public: RCIPtr(T *realPtr = 0); RCIPtr(const RCIPtr& rhs); ~RCIPtr(); RCIPtr& operator=(const RCIPtr& rhs); T* operator->() const { return counter->pointee; } T& operator*() const { return *(counter->pointee); } private: struct CountHolder : public RCObject { ~CountHolder() { delete pointee; } T *pointee; }; RCIPtr<T>::CountHolder *counter; void init(); }; template <class T> void RCIPtr<T>::init() { if (0 == counter) return; if (false == counter->isShareable() ) { counter = new CountHolder; counter->pointee = new T(*counter->pointee); } counter->addReference(); } template <class T> RCIPtr<T>::RCIPtr(T *realPtr) : counter(new CountHolder) { counter->pointee = realPtr; init(); } template <class T> RCIPtr<T>::RCIPtr(const RCIPtr& rhs) : counter(rhs.counter) { init(); } template <class T> RCIPtr<T>::~RCIPtr() { if (counter) counter->removeReference(); } template <class T> RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs) { if (counter != rhs.counter) { if (counter) counter->removeReference(); counter = rhs.counter; init(); } return *this; }
由于逻辑复杂化,引用计数对象创建时的开销增加了。