Efficient C++ 第十二章 引用计数

转自
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;
}


    由于逻辑复杂化,引用计数对象创建时的开销增加了。

猜你喜欢

转载自jacky-dai.iteye.com/blog/2307360