Reference counting的两个技术动机:
- 为了简化heap objects周边的簿记工作,当对象运用了引用计数技术,一旦不再有任何人使用它,它便自动销毁自己,也因此,reference counting建构出垃圾回收机制的一个简单形式;
- 为了实现一种常识。如果许多对象有相同的值,将那个值存储多次是一件愚蠢的事,最好是让所有等值对象共享一份实值即可,不仅节省内存,也会使得程序速度加快,因为不再需要构造和析构同值对象的多余副本。
由于需要记录“目前共享同一实值”的对象个数,一个reference count(引用计数器)就此引入:
Reference Counting(引用计数) 的实现
我们需要为每一个字符串值准备一个引用计数,而不是为每一个字符串对象准备,暗示了“对象值”和“引用计数”之间有一种耦合关系:产生一个class,不但存储引用计数,也存储它们追踪的对象值:
class String
{
public:... //一般的String成员函数安排在这里
private:struct StringValue{...}; //持有一个引用次数以及一个字符串值StringValue* value; //String的值
};
//String的定义
class String
{
private:struct StringValue{int refCount;char* data;};StringValue(const char* initValue);~StringValue();...
};//StringValue提供一个地点:
//将"某特定值"以及"共享该值的String对象个数"关联起来。
String::StringValue::StringValue(const char* initValue):refCount(1)
{data = new char[strlen(initValue) + 1];strcpy(data,initValue);
}String::StringValue::~StringValue()
{delete [] data;
}
//String的成员函数从构造函数开始:
class String
{
public:String(const char* initValue = "");String(const String& rhs);...
};//传入char*创建一个新的StringValue对象
String::String(const char* initValue) : value(new StringValue(initValue));
{}
//String的复制构造函数很有效率
String::String(const String& rhs):value(rhs.value)
{++value -> refCount;
}//运用
String s1("More Effective C++");
String s2(s1);
//新生成的String对象与被复制的对象共享相同的StringValue对象
以上只需将指针复制一份,并将引用从次数加1,不需要分配和归还内存,效率更高。
//String的析构函数
class String
{
public:~String();
};
String::~String()
{if(--value->refCount == 0) delete value;//只有当唯一的用户被析构,String析构函数才销毁StringValue
}
//String的赋值操作符
class String
{
public:...String& operator = (const String&& rhs);
};String& String::operator = (const String& rhs)
{if(value == rhs.value)return *this;if(--value->refCount == 0)delete value;value = rhs.value;++value->refCount;return *this;
}
Copy-On-Write(写时才复制)
方括号操作符[ ]
:它允许字符串中的个别字符被读取或被写;
class String
{
public:const char& operator[](int index) const;//针对const Stringschar& operator[](int index); //针对non-const String...
};//const版本:只读动作;字符串内容不受影响
const char& String::operator[](int index) const
{return value->data[index];
}//non-const版本:
//可能用来读取一个字符串,也可能用来写一个字符串
String s;
...
cout << s[3]; //读取动作
s[5] = 'x'; //写入动作
为了安全实现出non-const operator[]
,我们必须确保没有其他任何“共享同一个StringValue”的String对象因写动作而改变:
char& String::operator[](int index)
{//如果本对象和其他String对象共享同一实值//就分割(复制)出另一副本供本对象自己使用if(value->refCount > 1)--value->refCount; //将目前实值的引用频次减1,因为我们不再使用//为自己做一份新的副本value = new StringValue(value->data);//返回一个reference,代表我们这个“绝对不被共享”的//StringValue对象内的一个字符。return value->data[index];
}
Pointers,References,以及Copy-On-Write
为每一个StringValue对象加上一个标志(flag)变量,用以指示可否被共享:
//修改版
class String
{
private:struct StringValue{int refCount;bool shareable; //新增此行char* data;};StringValue(const char* initValue);~StringValue();...
};String::StringValue::StringValue(const char* initValue):refCount(1),shareable(true)//新增此行
{data = new char[strlen(initValue) + 1];strcpy(data,initValue);
}String::StringValue::~StringValue()
{delete [] data;
}//复制函数的新行为
String::String(const String& rhs)
{if(rhs.value->shareable){value = rhs.value;++value->refCount;}else{value = new StringValue(rhs.value->data);}
}//Non-const operator[]是唯一将shareble设为false者:
char& String::operator[](int index)
{if(value->refCount > 1)--value->refCount;value = new StringValue(value->data);value->shareable = false; //新增此行return value->data[index];
}
一个Reference-Counting(引用计数)基类
- Reference-counting可用于字符串以外的场合;
- 任何class如果其不同的对象可能拥有相同的值,都适用此技术。
产生一个base class RCObject,作为“reference-counted对象”之用。
任何class希望自动拥有reference counting能力,都必须继承自这个类:
//RCObject定义
class RCObject
{
public:RCObject();RCObject(const RCObject& rhs);RCObject& operator = (const RCObject& rhs);virtual ~RCObject() = 0;void addReference();void removeReference();void markUnshareable();bool isShareable() const;bool isShared() const;private:int refCount;bool shareable;
};//RCObject实现
RCObject::RCObject():refCount(0),shareable(true){}RCObject::RCObject(const RCObject& rhs):refCount(0),shareable(true){}RCObject& RCObject::operator = (const RCObject& rhs)
{ return *this; }RCObject::~RCObject(){}void RCObject::addReference()
{++refCount;
}void RCObject::removeReference()
{if(--refCount == 0)delete this;
}void RCObject::markUnshareable()
{shareable = false;
}bool RCObject::isShareable() const
{return shareable;
}bool RCObject::isShared() const
{return refCount > 1;
}//StringValue继承自RCObject
class String
{
private://StringValue的成员函数不再处理refCount字段,改由RCObject掌管struct StringValue: public RCObject{char* data;StringValue(const char* initValue);~StringValue();};...
};String::StringValue::StringValue(const char* initValue)
{data = new char[strlen(initValue) + 1];stpcpy(data,initValue);
}String::StringValue::~StringValue()
{delete [] data;
}
自动操作Reference Count(引用次数)
我们希望能够把这一系列调用动作移到一个可复用的class内,这么一来可以让诸如String之类的classes的作者不必操心reference counting的任何细节。
//下面这个模板用来产生智能指针指向reference-counted对象:
template<class 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;void init();
};//实现
template<T>
RCPtr<T>::RCPtr(T* realPtr = 0):pointee(realPtr)
{init();
}template<T>
RCPtr<T>::RCPtr(const RCPtr& rhs)pointee(rhs.pointee)
{init();
}template<T>
void RCPtr<T>::init()
{if(pointee == NULL) return ; if(pointee->isShareble() == false)pointee = new T(*pointee);pointee->addReference();
}
以上错误在于:init!
pointee = new T(*pointee)
构造新的T对象,但是RCPtr在String类内部,T将是String::StringValue,所以我们必须为String::StringValue加上深复制函数:
class String
{
private:struct StringValue: public RCObject{StringValue(const StringValue& rhs);...};
};String::StringValue::StringValue(const StringValue& rhs)
{data = new char[strlen(rhs.data) + 1];strcpy(data,rhs.data);
}
把所有努力放在一起
template<class T> //template class,用来产生
class RCPtr //smart pointers-to-Tobjects;
{ //T必须继承自RCObject
public:RCPtr(T* realPtr = 0);RCPtr(const RCPtr& rhs);~RCPtr();RCPtr& operator = (const RCPtr& rhs);T* operator->() const;T& operator*() const;private:T* pointee;void init();
};class RCObject //base class, 用于reference-counted objects
{
public:void addReference();void removeReference();void markUnshanreable();bool isShareable() const;bool isShared() const;protected:RCObject();RCObject(const RCObject& rhs);RCObject& operator = (const RCObject& rhs);virtual ~RCObject() = 0;private:int refCount;bool shareable;
};class String //应用性class,这是应用程序开发人员接触的层面
{
public:String(const char* value = "");const char& operator[](int index) const;char& operator[](int index);private:struct StringValue: public RCObject{char* data;StringValue(const char* initValue);StringValue(const StringValue& rhs);void init(const char* initValue);~StringValue();};RCPtr<StringValue> value;
};//RCObject实现
RCObject::RCObject():refCount(0),shareable(true){}RCObject::RCObject(const RCObject& rhs):refCount(0),shareable(true){}RCObject& RCObject::operator = (const RCObject& rhs)
{ return *this; }RCObject::~RCObject(){}void RCObject::addReference()
{ ++refCount; }
void RCObject::removeReference()
{if(--refCount == 0)delete this;
}void RCObject::markUnshanreable()
{ shareable = false; }
bool RCObject::isShareable() const
{return shareable;
}
bool RCObject::isShared() const
{return refCount > 1;
}//RCPtr实现
template<class T>
void RCPtr<T>::init()
{if(pointee == 0) return;if(pointee->isShareable == false)pointee = new T(*pointee);pointee->addReference;
}template<class T>
RCPtr<T>::RCPtr(T* realPtr = 0):pointee(realPtr)
{ init();
}template<class T>
RCPtr<T>::RCPtr(const RCPtr& rhs):pointee(realPtr)
{init();
}template<class T>
RCPtr<T>::~RCPtr()
{if(pointee)pointee->removeReference();
}template<class T>
RCPtr& RCPtr<T>::operator = (const RCPtr& rhs)
{if(pointee != rhs.pointee){if(pointee)pointee->removeReference();pointee = rhs.pointee;init();}return *this;
}template<class T>
T* RCPtr<T>::operator->() const
{ return pointee; }template<class T>
T& RCPtr<T>::operator*() const
{ return *pointee; }//String::StringValue实现
void String::StringValue::init(const char* initValue)
{data = new char[strlen(initValue) + 1];strcpy(data,initValue);
}String::StringValue::StringValue(const char* initValue)
{init(initValue);
}
String::StringValue::StringValue(const StringValue& rhs)
{init(rhs.data);
}String::StringValue::~StringValue()
{delete [] data;
}//String实现
String::String(const char* value = "")value(new StringValue(initValue)){}const char& String::operator[](int index) const
{return value->data[index];
}
char& String::operator[](int index)
{if(value->isShared()){value = new StringValue(value->data);}value->markUnshanreable();return value->data[index];
}
将Reference Counting加到既有的Classes身上
就像“StringValue只能实现细节,不需让String的用户知道”一样;
CountHolder也是实现细节,不需让RCWidget的用户知道。
事实上它是RCIPtr的实现细节,所以嵌套放入RCIPtr class内部。
RCIPtr实现如下:
template<class 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();
};template<class T>
void RCIPtr<T>::init()
{if(counter->isShareable == false){T* oldValue = counter->pointee;counter = new CountHolder;counter->pointee = new T(*pointee);} pointee->addReference;
}template<class T>
RCIPtr<T>::RCIPtr(T* realPtr):counter(new CountHolder)
{counter->pointee = realPtr;init();
}template<class T>
RCIPtr<T>::RCIPtr(const RCIPtr& rhs):counter(rhs.counter)
{init();
}template<class T>
RCIPtr<T>::~RCIPtr()
{counter->removeReference;
}template<class T>
RCIPtr& RCIPtr<T>::operator = (const RCIPtr& rhs)
{if(counter != rhs.counter){counter->removeReference();counter = rhs.counter;init();}return *this;
}template<class T>
const T* RCIPtr<T>::operator->() const
{return counter->pointee;
}template<class T>
const T& RCIPtr<T>::operator*() const
{return *(counter->pointee);
}
总结
什么时候最适合reference counting技术:
- 相对多数的对象共享相对少了的实值;
- 对象实值的产生或销毁成本很高,或使它们使用许多内存。