-
Reference counting这项技术,允许多个等值对象共享同一实值。此技术的发展有两种动机,第一是为了简化heap object周边的簿记工作。它可以消除“记录对象拥有权”的负荷,因为当对象运用了reference counting技术,它便拥有它自己。一旦不再有任何人使用它,它便会自动销毁自己。reference counting的第二个发展动机则只是为了实现一种常识。如果许多对象有相同的值,将那个值存储多次是件愚蠢的事。最好是让所有等值对象共享一份实值就好。
-
以下是一个包含了Copy-On-Write技术的Reference counting基类,有兴趣建议去看看原书中的文章,作者一步步引导写出这个类,写的很详细,这里因为篇幅原因就不赘述。
// RCObject.h
// template class,用来产生smart pointers-to-T objects;T必须继承自RCObject
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();
};
// base class,用于reference-counted objects
class RCObject {
public:
void addReference();
void removeReference();
void markUnshareable();
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;
};
// RCObject.cpp
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;
}
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) : pointee(realPtr) {
init();
}
template<class T>
RCPtr<T>::RCPtr(const RCPtr& rhs) : pointee(rhs.pointee) {
init();
}
template<class T>
RCPtr<T>::~RCPtr() {
if (pointee) {
pointee->removeReference();
}
}
template<class T>
RCPtr<T>& 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类,在String中包含表示其实值的struct StringValue,其中StringValue继承自上面的RCObject,使其拥有引用计数的能力。String代码如下所示。
// String.h
// 应用性class,这是应用程序开发人员接触的层面
#include <string.h>
#include "RCObject.h"
class String {
public:
String(const char *initValue = "");
const char& operator[] (int index) const;
char& operator[] (int index);
private:
// 以下struct用以表现字符串实值
struct StringValue : public RCObject {
char *data;
StringValue(const char *initValue);
StringValue(const StringValue& rhs);
void init(const char *initValue);
~StringValue();
};
RCPtr<StringValue> value;
};
// String.cpp
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(const char *initValue) : 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->markUnshareable();
return value->data[index];
}
- 再来写个main,简单验证下。
#include <String.h>
#include <iostream>
int main()
{
String s1 = "hello";
String s2 = s1;
std::cout << "s1[3] = " << s1[3] << std::endl;
std::cout << "s2[3] = " << s2[3] << std::endl;
s2[3] = 'x';
std::cout << "s1[3] = " << s1[3] << std::endl;
std::cout << "s2[3] = " << s2[3] << std::endl;
}
// 输出
s1[3] = l
s2[3] = l
s1[3] = l
s2[3] = x
- Reference counting是个优化技术,其使用前提是:对象常常共享实值。如果这个假设失败,reference counting反而会赔上更多内存,执行更多代码。从另一个角度看,如果你的对象确实有“共同实值”的倾向,reference counting应可同时节省你的时间和空间。
- 简单说,以下是使用reference counting改善效率的最适当时机:
- 相对多数的对象共享相对少量的实值(必要)。
- 对象实值的产生或销毁成本很高,或是它们使用许多内存(非必要)。