1. 程式人生 > >C++引用計數(reference counting)技術簡介(3)

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”間接。修改後的設計如下:

這裡寫圖片描述

引用計數基類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.總結

引用計數的實現需要成本。每一個擁有計數能力的實值都有一個引用計數器,而大部分操作都需要能夠以某種方式檢查或處理這個引用計數器,因此物件的實值需要更多記憶體。而且引用計數的底層原始碼比沒有引用計數的複雜的多。

引用計數是個優化計數,其適用前提是物件常常共享實值。使用引用計數改善效率的時機有以下兩個:
第一,相對多數的物件共享相對少量的實值;
第二,物件實值的產生或銷燬成本很高,或是它們使用許多記憶體。

參考文獻