more effective c++——Item M29 引用計數(一)和more effective c++——Item M29 引用計數(二)這兩篇都是針對char *的實現,雖然能夠正常工作,但是存在以下問題:
1.用戶類仍然需要構造自己的數據結構,並對值、計數器進行操作
2.不便於移植,需要對原始的類做特定的修改,不同的數據類型需要重新設計
3.必須要有權限修改數據類的源代碼,如果該類存在於lib中,則無法實現
是否可以設計一種新的類,達到引用計數的效果的同時避免上述問題,並且對用戶屏蔽該類的實現?stanly lippman提供了帶引用計數的基類的方法來解決這些問題——通過構建一個智能指針類來代理原始類,
帶引用計數基類的String類
一.構建引用計數的基類RCObject
1.RCObject作爲引用計數的基類,所有的引用計數類都從它繼承
2.RCObject只提供基本的操作,但是不對計數器及資源做任何操作
3.RCObject提供該對象是否可共享的標記符,並提供相應操作接口,RCObject對象默認可共享
4.作爲基類爲了實現動態綁定,RCObject需要作爲純虛類實現,不可實例化
5.RCObject爲了能夠在引用計數爲0的時候刪除資源,必須調用delete this,則RCObject子類必須建立在堆上
6.當構造一個RCObject子類時,我們並不知道子類要如何設置引用計數,因此我們將RCObject初始化引用計數爲0,並由持有rcobject子類的類去負責引用計數的增減及資源的釋放
RCObject類實現如下:
#pragma once
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()
: refCount(0), shareable(true) {}
RCObject::RCObject(const RCObject&)
: refCount(0), shareable(true) {}
RCObject& RCObject::operator=(const RCObject&)
{
return *this;
}
RCObject::~RCObject() {} // virtual dtors must always
// be implemented, even if
// they are pure virtual
// and do nothing (see also
// Item M33 and Item E14)
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;
}
RCPtr 的實現
有了引用計數的基類,可以開始着手實現持有rcobject對象的類了,該類負責引用計數的增減及對象的構造與釋放,它相當於一個簡易的智能指針,主要實現以下功能:
1.原始指針作爲參數的構造函數
2.由於持有堆內存上的對象,因此需要實現拷貝構造函數與賦值操作符
3.析構函數,負責根據引用計數的值判斷是否需要刪除堆上的資源
4.提供operator->與operator*操作符,用於模擬指針操作
5.爲了不限於爲某一類型提供服務,實現爲模板類,且其模板類型必須從rcobject繼承
實現如下:
#pragma once
// template class for smart pointers-to-T objects. T must
// support the RCObject interface, typically by inheriting from RCObject
template<class T>
class RCPtr {
public:
RCPtr(T* realPtr = 0);
RCPtr(const RCPtr& rhs);
~RCPtr();
RCPtr& operator=(const RCPtr& rhs);
T* operator->() const; // see Item 28
T& operator*() const; // see Item 28
private:
T *pointee; // dumb pointer this object is emulating
void init(); // common initialization
};
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>
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>::operator=(const RCPtr& rhs)
{
if (pointee != rhs.pointee) {
if (pointee) {
pointee->removeReference(); // remove reference
}
pointee = rhs.pointee;
init();
}
return *this;
}
template<class T>
RCPtr<T>::~RCPtr()
{
if (pointee)pointee->removeReference();
}
template<class T>
T* RCPtr<T>::operator->() const { return pointee; }
template<class T>
T& RCPtr<T>::operator*() const { return *pointee; }
有了引用計數的基類及智能指針,以前實現的stringvalue則不再需要持有rfcount,只需要從它繼承,然後持有必要的資源,讓rcptr負責引用計數與構造析構相關的工作即可,以下爲stringvalue及String類的實現:
#pragma once
#include "RCObject.h"
#include "RCPtr.h"
#include <cstring>
class String { // class to be used by application developers
public:
String(const char *value = "");
const char& operator[](int index) const;
char& operator[](int index);
private:
// class representing string values
struct StringValue : public RCObject {
char *data;
StringValue(const char *initValue);
StringValue(const StringValue& rhs);
void init(const char *initValue);
~StringValue();
};
RCPtr<StringValue> value;
};
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];
}
通用引用計數的實現
上面的類針必須要能夠修改原始代碼,在不可修改原始代碼的情況下,增加一箇中間層即可實現同樣的功能,而且使用起來更加方便,因爲類的相關信息都被屏蔽,新實現的類不需要關心以何種結構來承載資源與引用計數
計算機科學中的絕大部分問題都可以通過增加一箇中間層次來解決。由於不能直接修改原始類——OldClass的代碼,可以通過增加一箇中間層CountHolder ,讓它持有需要修改的類的指針,然後讓智能指針類RCIPtr的模板對象持有該計數器的指針,然後將RCIPtr<OldClass>作對象爲新實現的引用計數類RFNewClass的成員變量,通過RFNewClass間接持有的OldClass指針實現與OldClass相同的接口即可。
//RCIPtr.hpp實現,
#pragma once
#include "RCObject.h"
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(); // see below
};
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;
}
template<class T> // implement the copy
void RCIPtr<T>::makeCopy() // part of copy-on-
{ // write (COW)
if (counter->isShared()) {
T *oldValue = counter->pointee;
counter->removeReference();
counter = new CountHolder;
counter->pointee = new T(*oldValue);
counter->addReference();
}
}
template<class T> // const access;
const T* RCIPtr<T>::operator->() const // no COW needed
{
return counter->pointee;
}
template<class T> // non-const
T* RCIPtr<T>::operator->() // access; COW
{
makeCopy(); return counter->pointee;
} // needed
template<class T> // const access;
const T& RCIPtr<T>::operator*() const // no COW needed
{
return *(counter->pointee);
}
template<class T> // non-const
T& RCIPtr<T>::operator*() // access; do the
{
makeCopy(); return *(counter->pointee);
}
RCIPtr的使用:
#pragma once
#include "RCIPtr.h"
#include <string>
using namespace std;
class RCString {
public:
RCString(const char *str = "") : value(new string(str)) {}
private:
RCIPtr<string> value;
};