前四篇所敘述的每一件事情,都必須動用到我們感興趣的那個class的源代碼.但如果我們想要將reference counting施行於程序庫中一個名爲Widget的class呢?程序庫的行爲不是我們可以更改的,所以沒辦法讓Widget繼承自RCObject,也就無法對Widget使用smart RCPtrs.
但只要稍微修改設計,我們就可以爲任何類型加上reference counting能力.
首先,讓我們考慮,如果令Widget繼承自RCObject,我們的設計看起來將如何.這種情況下,我們必須增加一個RCWidget class給clients使用,但每件事情都極類似先前的String/StringValue例子:RCWidget扮演String角色,Widget扮演StringValue的角色.整個設計看起來去如下:
現在我們可以將”計算機科學領域中大部分問題得以解決”的原理施展出來.我們可以加上一層間接性.是的,我們增加一個新的CountHolder class,用以持有引用次數,並令CountHolder繼承自RCObject.我們也令CountHolder內含一個指針,指向一個Widget.然後將smart RCPtr template以同樣聰明的RCIPtr template取代,後者知道CountHolder class 的存在.RCIPtr的”I”意指”indirect”(間接).修改後的設計如下:
就像”StringValue只是實現細節,不需要讓String的用戶知道”一樣,CountHolder也是實現細節,不需要讓RCWidget的用戶知道.事實上,它是RCIPtr的實現細節,所以我們把它嵌套放進RCIPtr class的內部.RCIPtr的聲明如下:
template<typename T>
class RCIPtr
{
public:
RCIPtr(T* realPtr = nullptr);
RCIPtr(const RCIPtr& rhs_);
RCIPtr& operator=(const RCIPtr& rhs_);
~RCIPtr();
public:
const T* operator->() const;
T* operator->();
const T& operator*() const;
T& operator*();
private:
void init();
void makeCopy();
private:
struct CountHolder : public RCObject
{
T* ptr;
~CountHolder()
{
if (ptr != nullptr)
{
delete ptr;
ptr = nullptr;
}
}
};
CountHolder* _counter;
};
RCIPtr和RCPtr之間存在兩個差異.第一,”RCPtr對象”之間指向實值,而”RCIPtr對象”通過中介層”CountHolder”對象指向實值;第二,RCIPtr將operator->和operator*重載了,如此一來,只要有non-const access發生於被指物上,copy-on-write(寫時進行復制)就會自動執行.
RCIPtr定義如下:
template<typename T>
void RCIPtr<T>::init()
{
if (_counter->isShareable() == false)
{
auto oldptr = _counter->ptr;
_counter = new CountHolder();
_counter->ptr = new T(*oldptr);
}
_counter->addReference();
}
template<typename T>
RCIPtr<T>::RCIPtr(T* realPtr /* = nullptr */)
: _counter(new CountHolder())
{
_counter->ptr = realPtr;
init();
}
template<typename T>
RCIPtr<T>::RCIPtr(const RCIPtr& rhs_)
: _counter(rhs_._counter)
{
init();
}
template<typename T>
RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs_)
{
if (_counter ! = rhs_._counter)
{
_counter->removeReference();
_counter = rhs_._counter;
init();
}
return *this;
}
template<typename T>
RCIPtr<T>::~RCIPtr()
{
_counter->removeReference();
}
template<typename T>
const T* RCIPtr<T>::operator->() const
{
return _counter->ptr;
}
template<typename T>
T* RCIPtr<T>::operator->()
{
makeCopy();
return _counter->ptr;
}
template<typename T>
const T& RCIPtr<T>::operator*() const
{
return *(_counter->ptr);
}
template<typename T>
T& RCIPtr<T>::operator*()
{
makeCopy();
return *(_counter->ptr);
}
template<typename T>
void RCIPtr<T>::makeCopy()
{
if (_counter->isShared())
{
auto oldptr = _counter->ptr;
_counter->removeReference();
_counter = new CountHolder();
_counter->ptr = new T(*oldptr);
_counter->addReference();
}
}
有了RCIPtr,RCWidget的實現就很容易了,因爲RCWidget的每一個函數都只是通過底層的RCIPtr轉調對應的Widget函數.示例如下:
class Widget
{
public:
void doThis()
{
std::cout << "doThis" << std::endl;
}
};
class RCWidget
{
public:
void doThis()
{
_value->doThis();
}
private:
RCIPtr<Widget> _value;
};
對於String類,我們只需要將C++ 簡易string類實現(一)參考上述Widget和RCWidget的例子,生成一個對應的RCString就OK了;
小結:通過不斷的抽象,最終將引用計數類和用戶自定義類完全解耦,而且引用計數類還可以重用於其它用戶自定義類,簡直 完美!!!
完整代碼如下:
class RCObject
{
public:
RCObject();
RCObject(const RCObject& rhs_);
//使RCObject成爲抽象基類,但該純虛函數需要提供
//定義,不然會使被繼承的類無法在棧上創建(原因可
//查閱如何僅在堆上或棧上分配內存)
virtual ~RCObject() = 0;
public:
void addReference();
void removeReference();
void markUnshareable();
bool isShareable() const;
bool isShared() const;
private:
RCObject& operator=(const RCObject&) = delete;
private:
int refCount;
bool shareable;
};
template<typename T>
class RCIPtr
{
public:
RCIPtr(T* realPtr = nullptr);
RCIPtr(const RCIPtr& rhs_);
RCIPtr& operator=(const RCIPtr& rhs_);
~RCIPtr();
public:
const T* operator->() const;
T* operator->();
const T& operator*() const;
T& operator*();
private:
void init();
void makeCopy();
private:
struct CountHolder : public RCObject
{
T* ptr;
~CountHolder()
{
if (ptr != nullptr)
{
delete ptr;
ptr = nullptr;
}
}
};
CountHolder* _counter;
};
RCObject::RCObject()
:refCount(0), shareable(true) //refCount初始化爲0,其值完全有RCPtr控制
{
}
RCObject::RCObject(const RCObject&)
//調用無參構造函數,注意:該調用僅能在
//初始化成員列表裏,如果在函數實現內調用,
//那麼僅僅是在棧上生成新的對象,而不是完成
//該對象的成員初始化
:RCObject()
{
std::cout << "RCObject" << std::endl;
}
RCObject::~RCObject()
{
//std::cout << "~RCObject" << std::endl;
}
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<typename T>
void RCIPtr<T>::init()
{
if (_counter->isShareable() == false)
{
auto oldptr = _counter->ptr;
_counter = new CountHolder();
_counter->ptr = new T(*oldptr);
}
_counter->addReference();
}
template<typename T>
RCIPtr<T>::RCIPtr(T* realPtr /* = nullptr */)
: _counter(new CountHolder())
{
_counter->ptr = realPtr;
init();
}
template<typename T>
RCIPtr<T>::RCIPtr(const RCIPtr& rhs_)
: _counter(rhs_._counter)
{
init();
}
template<typename T>
RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs_)
{
if (_counter ! = rhs_._counter)
{
_counter->removeReference();
_counter = rhs_._counter;
init();
}
return *this;
}
template<typename T>
RCIPtr<T>::~RCIPtr()
{
_counter->removeReference();
}
template<typename T>
const T* RCIPtr<T>::operator->() const
{
return _counter->ptr;
}
template<typename T>
T* RCIPtr<T>::operator->()
{
makeCopy();
return _counter->ptr;
}
template<typename T>
const T& RCIPtr<T>::operator*() const
{
return *(_counter->ptr);
}
template<typename T>
T& RCIPtr<T>::operator*()
{
makeCopy();
return *(_counter->ptr);
}
template<typename T>
void RCIPtr<T>::makeCopy()
{
if (_counter->isShared())
{
auto oldptr = _counter->ptr;
_counter->removeReference();
_counter = new CountHolder();
_counter->ptr = new T(*oldptr);
_counter->addReference();
}
}