我们可以使用这句格言:计算机科学中的绝大部分问题都可以通过增加一个中间层次来解决。我们增加一个
新类CountHolder以处理引用计数,它从RCObject继承。我们让CountHolder包含一个指针指向Widget,将CountHolder
二、在现存类上增加引用计数
RCIPtr的实现如下:
1)引用计数在下列情况对提高效率有用:
少量的值被大量的对象共享。这样的共享通常通过调用赋值操作和拷贝构造而发生。对象/值的比例越高,
越是适宜使用引用计数。使用profiler或其他工具分析是否创建和销毁值的行为是性能瓶颈,并能得出对象/值
的比例。
2)有些数据结构(如有向图)将导致自我引用或环状结构。这样的数据结构可能导致孤立的自引用对象,它没有
被别人使用,而其引用计数又绝不会降到零。因为这个无用的结构中的每个对象被同结构中的至少一个对象所引用。
商业化的垃圾收集体系使用特别的技术来查找这样的结构并消除它们。
3)使用引用计数可以不用担心谁去执行删除操作。
新类CountHolder以处理引用计数,它从RCObject继承。我们让CountHolder包含一个指针指向Widget,将CountHolder
类指针封装在智能指针RCIPtr(名字中的"i"表示间接"indirect")内。设计图如下:
一、带引用计数的基类
RCObject的定义如下:
class RCObject
{
public:
RCObject();
RCObject(const RCObject& rhs);
RCObject& operator=(const RCObject& rhs);
virtual ~RCObject() = 0;
void addReference();
void removeReference();
void markUnshareavle();
bool isShareable() const;
bool isShared() const;
private:
int refCount; // 引用计数
bool shareable; // 是否共享的标志
};
RCObject::RCObject() : refCount(0), shareable(true) {}
RCObject::RCObject(const RCObject&) : refCount(0), shareable(true) {}
RCObject& RCObject::operator=(const RCObject&) { 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; }
二、在现存类上增加引用计数
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(*oldValue);
}
counter->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<T>& RCIPtr<T>::operator=(const RCIPtr& rhs)
{
if(counter != rhs.counter)
{
counter->removeReference();
counter = rhs.counter;
init();
}
return *this;
}
// 实现copy-on-write(COW)的拷贝部分
template<class T>
void RCIPtr<T>::makeCopy()
{
if(counter->isShared())
{
T* oldValue = conter->pointee;
counter->removeReference();
counter = new CountHolder;
counter->pointee = new T(*oldValue);
counter->addReference();
}
}
// const访问,不需要写时复制
template<class T>
const T* RCIPtr<T>::operator->() const
{
return counter->pointee;
}
// 非const访问,需要写时复制
template<class T>
T* RCIPtr<T>::operator->()
{
makeCopy();
return counter->pointee;
}
// const访问,不需要写时复制
template<class T>
const T& RCIPtr<T>::operator*() const
{
return *(counter->pointee);
}
// 非const访问,需要写时复制
template<class T>
T& RCIPtr<T>::operator*()
{
makeCopy();
return *(counter->pointee);
}
RCIPtr重载了operator->和operator*,当有对被指向的对象的非const的操作时,写时拷贝自动执行。
有了RCIPtr,很容易实现RCWidget,因为RCWidget的每个函数都是将调用传递给RCIPtr以操作Widget对象。
举个例子,如果Widget是这样的:
class Widget
{
public:
Widget(int size);
Widget(const Widget& rhs);
~Widget();
Widget& operator=(const Widget& rhs);
void doThis();
int showThat() const;
};
class RCWidget
{
public:
RCWidget(int size) : value(new Widget(size)) {}
void doThis() { value->doThis(); }
int showThat() const { return value->showThat(); }
private:
RCIPtr<Widget> value;
};
1)引用计数在下列情况对提高效率有用:
少量的值被大量的对象共享。这样的共享通常通过调用赋值操作和拷贝构造而发生。对象/值的比例越高,
越是适宜使用引用计数。使用profiler或其他工具分析是否创建和销毁值的行为是性能瓶颈,并能得出对象/值
的比例。
2)有些数据结构(如有向图)将导致自我引用或环状结构。这样的数据结构可能导致孤立的自引用对象,它没有
被别人使用,而其引用计数又绝不会降到零。因为这个无用的结构中的每个对象被同结构中的至少一个对象所引用。
商业化的垃圾收集体系使用特别的技术来查找这样的结构并消除它们。
3)使用引用计数可以不用担心谁去执行删除操作。