C++引用计数(reference counting)技术简介(3)

1.将Reference Counting加到既有的Class

要想将引用计数施加到现有的实值对象Widget上,按照前面讨论的,都需要修改Winget类的源代码。但是,有时程序库的内容不是我们呢可以修改的,又该如何做呢?

如果令Widget继承自RCObject,我们必须增加一个RCWidget class给用户使用,这很像之前关于String/StringValue的讨论。RCWidget扮演String的角色,Widget扮演StringValue的角色。整个设计结构如下:

但这么做的话就需要修改Widget使其继承自RCObject。我们可以增加一个新的CountHolder class,用以持有引用计数,并令CountHolder继承自RCObject。我们也令CountHolder内含一个指针,指向一个Widget。然后将smart RCPtr template以同样聪明的RCIPtr template取代,RCIPtr template包含CountHolder的一个指针。RCIPtr 的”I”意指”indirect”间接。修改后的设计如下:

C++引用计数(reference counting)技术简介(3)_第1张图片

引用计数基类RCObject基本不变,其源码如下:

//引用计数基类 
class RCObject{  
public:  
    RCObject();//构造函数 
    RCObject(const RCObject& rhs);//拷贝构造函数 
    RCObject& operator=(const RCObject& rhs);//拷贝赋值运算符 
    virtual ~RCObject() = 0;//析构函数

    void addReference();//增加引用计数 
    void removeReference();//减少引用计数,如果变为0,销毁对象 
    void markUnshareable();//将可共享标志设为false 
    bool isShareable() const;//判断其值是否可共享 
    bool isShared() const;//判断其值是否正在被共享 
    int getRefCount();//返回引用计数 
private:  
    int refCount;//保存引用计数 
    bool shareable;//保存其值是否可共享的状态 
};  

//构造函数,这里refCount设为0,让对象创建者自行或将refCoun设为1 
RCObject::RCObject(void) :refCount(0), shareable(true){}  

//拷贝构造函数,总是将refCount设为0,因为正在产生一个新对象,只被创建者引用 
RCObject::RCObject(const RCObject&) : refCount(0), shareable(true){}  

//拷贝赋值运算符,这里只返回*this,因为左右两方RCObject对象的外围对象个数不受影响 
RCObject& RCObject::operator=(const RCObject& rhs){  
    return *this;  
}  

//析构函数 
RCObject::~RCObject(){}  

//增加引用计数 
void RCObject::addReference(){  
    ++refCount;  
}  

//减少引用计数,如果变为0,销毁对象 
void RCObject::removeReference(){  
    if (--refCount == 0)  
        delete this;  
}  

//将追踪其值是否可共享的成员设为false 
void RCObject::markUnshareable(){  
    shareable = false;  
}  

//判断其值是否可共享 
bool RCObject::isShareable() const{  
    return shareable;  
} 

//判断其值是否正在被共享 
bool RCObject::isShared() const{  
    return refCount>1;  
}  

//返回引用计数 
int RCObject::getRefCount(){  
    return refCount;  
}  

template RCIPtr的实现如下:

//智能指针模板类,用来自动执行引用计数类成员的操控动作 
template<typename T>  
class RCIPtr{  
public:  
    RCIPtr(T* realPtr = 0);//构造函数 
    RCIPtr(const RCIPtr& rhs);//拷贝构造函数 
    ~RCIPtr();//析构函数 
    RCIPtr& operator=(const RCIPtr& rhs);//拷贝赋值运算符 
    const T* operator->() const;//重载->运算符 
    T* operator->();//重载->运算符 
    const T& operator*() const;//重载*运算符 
    T& operator*();//重载*运算符 
private:  
    struct CountHolder :public RCObject{  
        ~CountHolder() { delete pointee; }  
        T* pointee;  
    };  
    CountHolder* counter;  
    void init();//初始化操作 
    void makeCopy();//copy-on-write中的copy部分 
};  
//共同的初始化操作 
template <typename T>  
void RCIPtr<T>::init(){  
    if (counter->isShareable() == false){  
        T* oldValue = counter->pointee;  
        counter = new CountHolder;  
        counter->pointee = new T(*oldValue);  
    }  
    counter->addReference();  
}  
//构造函数 
template <typename T>  
RCIPtr<T>::RCIPtr(T* realPtr) :counter(new CountHolder){  
    counter->pointee = realPtr;  
    init();  
}  
//拷贝构造函数 
template <typename T>  
RCIPtr<T>::RCIPtr(const RCIPtr& rhs) :counter(rhs.counter){  
    init();  
}  
//析构函数 
template <typename T>  
RCIPtr<T>::~RCIPtr(){  
    counter->removeReference();  
}  
//拷贝赋值运算符 
template <typename T>  
RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs){  
    if (counter != rhs.counter){  
        counter->removeReference();  
        counter = rhs.counter;  
        init();  
    }  
    return *this;  
}  
//重载->运算符,const版本 
template<typename T>  
const T* RCIPtr<T>::operator->() const { return counter->pointee; }  
//重载*运算符,non-const版本 
template<typename T>  
const T& RCIPtr<T>::operator*() const { return *(counter->pointee); }  
//copy-on-write中的copy部分 
template <typename T>  
void RCIPtr<T>::makeCopy(){  
    if (counter->isShared()){  
        T* oldValue = counter->pointee;  
        counter->removeReference();  
        counter = new CountHolder;  
        counter->pointee = new T(*oldValue);  
        counter->addReference();  
    }  
}  
//重载->运算符,non-const版本 
template <typename T>  
T* RCIPtr<T>::operator->(){  
    makeCopy();  
    return counter->pointee;  
}  
//重载*运算符,non-const版本 
template <typename T>  
T& RCIPtr<T>::operator*(){  
    makeCopy();  
    return *(counter->pointee);  
}  

RCIPtr和RCPtr之间有两个差异,一个是RCPtr对象直接指向实值,而RCIPtr对象通过中介层“CountHolder对象”指向实值。第二是RCIPtr将operator->和operator*重载了,如此一来只要有non-const access发生在被指物身上,copy-on-write(写时复制)就会被执行。

有了RCIPtr,RCWidget的实现就很容易,因为RCWidget的每一个函数都只是通过底层的RCIPtr转调用对应的Widget函数。Widget和RCWidget的示例代码如下。

class Widget{  
public:  
    Widget(int s = 0) :size(s){}  
    Widget(const Widget& rhs) { size = rhs.size; }  
    ~Widget(void) {}  

    Widget& operator=(const Widget& rhs){  
        if (this == &rhs)  
            return *this;  
        this->size = rhs.size;  
        return *this;  
    }  
    void doThis() { std::cout << "doThis()" << std::endl; }  
    int showThat() const {   
        std::cout << "showThat()" << std::endl;   
        return size;   
    }  
private:  
    int size;  
};  

class RCWidget{  
public:  
    RCWidget(int size = 0) :value(new Widget(size)){}  
    ~RCWidget() {}  

    void doThis() { value->doThis(); }  
    int showThat() const { return value->showThat(); }  
private:  
    RCIPtr<Widget> value;  
};  

注意,RCWidget没有申明拷贝构造函数,也没有赋值运算符和析构函数,就像先前的String class一样,不再需要撰写这些函数了,因为编译器生成的默认版本做了正确的事情。

2.总结

引用计数的实现需要成本。每一个拥有计数能力的实值都有一个引用计数器,而大部分操作都需要能够以某种方式检查或处理这个引用计数器,因此对象的实值需要更多内存。而且引用计数的底层源代码比没有引用计数的复杂的多。

引用计数是个优化计数,其适用前提是对象常常共享实值。使用引用计数改善效率的时机有以下两个:
第一,相对多数的对象共享相对少量的实值;
第二,对象实值的产生或销毁成本很高,或是它们使用许多内存。

参考文献

[1]More Effective C++.Scott Meyers著,侯捷译.P183-213.
[2]http://blog.csdn.net/ruan875417/article/details/48267527.

你可能感兴趣的:(C++引用计数(reference counting)技术简介(3))