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

版权声明:感谢您对博文的关注!校招与社招,有需要内推腾讯的可以QQ(1589276509)or 微信(louislvlv)联系我哈,期待您的加入。 https://blog.csdn.net/K346K346/article/details/50623486

1.一个引用计数(Reference-Counting)基类

Reference-counting可用于字符串以外的场合,任何class如果其不同的对象可能拥有相同的值,都适用此技术。但是如果重写class以便适用reference counting可能需要大量的工作。

我们可以设计一个引用计数基类RCObject,供想拥有引用计数的类继承。RCObject将“引用计数器”本身以及用以增减引用数值的函数封装起来。此外,还包括销毁对象值的函数,设置不可共享标的函数,返回共享标志的函数,查询是否在被共享的函数,查询引用计数的数目。没有必要提供一个设定共享标志位true的成员函数,因为所有的对象值在默认情况下都是可共享的。这里设定一旦某个对象被贴上”不可共享”标签,其永远都将是不可共享。

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

注意:
(1)RCObject的赋值运算符opeator=()什么也没有做,实际上可共享的实值实际不太可能被赋值。例如在自定义String类中,实值StringValue并不不会被赋值,而是String对象的赋值。

(2)RCObject::removeReference的责任不只在于将对象的refCount递减,而有当引用计数refCount为0时,销毁实值对象。使用delete this来销毁实值对象,那就要求*this是heap对象。

2.基于引用计数基类的String

基于引用计数的基类String设计如下:

class String{
private:
    Struct StringValue:public RCObject{
        char* data;

        StringValue(const char* initValue);
        ~StringValue();
    };
    StringValue* value;  

public:
    String(const char* initValue="");//constructor
    String(const String& rhs);//copy constructor
    String& operator=(const String& rhs); //assignment operator
    ~String(); //destructor
};

//StringValue的构造函数
String::StringValue::StringValue(const char* initValue):refCount(1){
     data=new char[strlen(initValue)+1];
     strcpy(data,initValue);
 }

//StringValue的析构函数
String::StringValue::~StringValue(){
    delete[] data;
}

这一版本的StringValue几乎与前一版本完全相同,唯一的改变是StringValue的member functions不再处理引用计数refCount字段,改由RCObject掌握。

3.自动操作引用次数(Reference Count)

RCObject class存放了引用次数,也给出了操作引用次数的member fucntions,这些函数的调用动作还是得用户手动写到其他的class内,并且通过String constructor和String assignment operator调用StringValue对象所提供的addReference和removeReference。这里,我们使用可复用的类,不必让用户类去操作引用次数。这里可复用的类产生的对象我们呢称之为smart pointer。

下面使用template来实现smart pointers,指向reference-counted实值对象。

//智能指针模板类,用来自动执行引用计数实值类成员的操控动作  
template<typename T>                        
class RCPtr{                           
public:               
    RCPtr(T* realPtr = 0);//构造函数  
    RCPtr(const RCPtr& rhs);//拷贝构造函数  
    ~RCPtr();//析构函数  
    RCPtr& operator=(const RCPtr& rhs);//拷贝赋值运算符  
    T* operator->() const;//重载->运算符  
    T& operator*() const;//重载*运算符  
private:  
    T* pointee;  //dumb pointer
    void init(); //共同的初始化操作  
};  
//共同的初始化操作  
template<typename T>  
void RCPtr<T>::init(){  
    if (pointee == 0) return;  
    if (pointee->isShareable() == false) {  
        pointee = new T(*pointee);  
    }  
    pointee->addReference();  
}  
//构造函数  
template<typename T>  
RCPtr<T>::RCPtr(T* realPtr) :pointee(realPtr){  
    init();  
}  
//拷贝构造函数  
template<typename T>  
RCPtr<T>::RCPtr(const RCPtr& rhs) : pointee(rhs.pointee){  
    init();  
}  
//析构函数  
template<typename T>  
RCPtr<T>::~RCPtr(){  
    if (pointee)  
        pointee->removeReference();  
}  
//赋值运算符  
template<typename T>  
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs){  
    if (pointee != rhs.pointee) {  
        if (pointee)  
            pointee->removeReference();  
        pointee = rhs.pointee;  
        init();  
    }  
    return *this;  
}  
//重载成员选取运算符 -> 
template<typename T>  
T* RCPtr<T>::operator->() const { return pointee; }  
//重载解引用运算符*  
template<typename T>  
T& RCPtr<T>::operator*() const { return *pointee; }  

4.最终String

在上面的基础之上,我们利用具有服用性质的RCObject和RCPtr classes为基础,建造一个reference-counted String class。每一个具有引用计数功能的String对象均以此数据结构实现出来:
这里写图片描述

最终的String描述如下:

class String {                             
public:                                  
    String(const char *value = "");//构造函数  
    const char& operator[](int index) const;//重载[]运算符,针对const Strings  
    char& operator[](int index);//重载[]运算符,针对non-const Strings  
private:  
    struct StringValue : public RCObject {//继承自引用计数基类  
        char *data;  

        StringValue(const char *initValue);//构造函数  
        StringValue(const StringValue& rhs);//拷贝赋值运算符  
        void init(const char *initValue);  
        ~StringValue();//析构函数  
    };  
    RCPtr<StringValue> value;//智能指针对象  
};  

//String::StringValue实现代码  
void String::StringValue::init(const char *initValue){  
    data = new char[strlen(initValue) + 1];  
    strcpy(data, initValue);  
}  
//StringValue类的构造函数  
String::StringValue::StringValue(const char *initValue){  
    init(initValue);  
}  
//StringValue类的拷贝赋值运算符  
String::StringValue::StringValue(const StringValue& rhs){  
    init(rhs.data);  
}  
//StringValue类的析构函数  
String::StringValue::~StringValue(){  
    delete[] data;  
}  

//String实现代码  
//String类的构造函数  
String::String(const char *initValue): value(new StringValue(initValue)) {}  
//重载[]运算符,针对const Strings  
const char& String::operator[](int index) const{  
    return value->data[index];  
}  
//重载[]运算符,针对non-const Strings  
char& String::operator[](int index){  
    if (value->isShared()) {  
        value = new StringValue(value->data);  
    }  
    value->markUnshareable();  
    return value->data[index];  
}  

注意,这里使用智能指针对象的String并不需要显示定义copy constructor和assignment operator,因为这些编译器为默认生成,并且会自动调用String内RCPtr member的copy constructor和assignment operator,而后者又会自动执行对StringValue对象的所有处理,包括引用次数。


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

展开阅读全文

没有更多推荐了,返回首页