- 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改善效率的最适当时机:
- 相对多数的对象共享相对少量的实值(必要)。
- 对象实值的产生或销毁成本很高,或是它们使用许多内存(非必要)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。