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对象。
基于引用计数的基类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掌握。
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; }
在上面的基础之上,我们利用具有服用性质的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.