C++ 引用计数技术简介(2/3)

1.一个引用计数基类

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.自动操作引用次数

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对象的所有处理,包括引用次数。


参考文献

More Effective C++.Scott Meyers著,侯捷译.P183-213
more effective c++读书笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值