【C++ techniques】Reference counting(引用计数)

Reference counting的两个技术动机:

  1. 为了简化heap objects周边的簿记工作,当对象运用了引用计数技术,一旦不再有任何人使用它,它便自动销毁自己,也因此,reference counting建构出垃圾回收机制的一个简单形式;
  2. 为了实现一种常识。如果许多对象有相同的值,将那个值存储多次是一件愚蠢的事,最好是让所有等值对象共享一份实值即可,不仅节省内存,也会使得程序速度加快,因为不再需要构造和析构同值对象的多余副本。

由于需要记录“目前共享同一实值”的对象个数,一个reference count(引用计数器)就此引入:

Reference Counting(引用计数) 的实现

我们需要为每一个字符串值准备一个引用计数,而不是为每一个字符串对象准备,暗示了“对象值”和“引用计数”之间有一种耦合关系:产生一个class,不但存储引用计数,也存储它们追踪的对象值:

class String
{
public:
	... //一般的String成员函数安排在这里
private:
	struct StringValue{...};	 //持有一个引用次数以及一个字符串值
	StringValue* value;   		 //String的值
};
//String的定义
class String
{
private:
	struct StringValue
	{
		int refCount;
		char* data;
	};
	StringValue(const char* initValue);
	~StringValue();
	...
};

//StringValue提供一个地点:
//将"某特定值"以及"共享该值的String对象个数"关联起来。
String::StringValue::StringValue(const char* initValue)
		:refCount(1)
{
	data = new char[strlen(initValue) + 1];
	strcpy(data,initValue);
}
 
String::StringValue::~StringValue()
{
	delete [] data;
}
//String的成员函数从构造函数开始:
class String
{
public:
	String(const char* initValue = "");
	String(const String& rhs);
	...
};

//传入char*创建一个新的StringValue对象
String::String(const char* initValue) : value(new StringValue(initValue));
{}
//String的复制构造函数很有效率
String::String(const String& rhs):
	value(rhs.value)
{
	++value -> refCount;
}

//运用
String s1("More Effective C++");
String s2(s1);
//新生成的String对象与被复制的对象共享相同的StringValue对象

以上只需将指针复制一份,并将引用从次数加1,不需要分配和归还内存,效率更高。

//String的析构函数
class String
{
public:
	~String();
};
String::~String()
{
	if(--value->refCount == 0) delete value;
	//只有当唯一的用户被析构,String析构函数才销毁StringValue
}
//String的赋值操作符
class String
{
public:
	...
	String& operator = (const String&& rhs);
};

String& String::operator = (const String& rhs)
{
	if(value == rhs.value)
		return *this;
	
	if(--value->refCount == 0)
		delete value;
	
	value = rhs.value;
	++value->refCount;
	return *this;
}

Copy-On-Write(写时才复制)

方括号操作符[ ]:它允许字符串中的个别字符被读取或被写;

class String
{
public:
	const char& operator[](int index) const;//针对const Strings
	char& operator[](int index);			//针对non-const String
	...
};

//const版本:只读动作;字符串内容不受影响
const char& String::operator[](int index) const
{
	return value->data[index];
}

//non-const版本:
//可能用来读取一个字符串,也可能用来写一个字符串
String s;
...
cout << s[3];	//读取动作
s[5] = 'x';		//写入动作

为了安全实现出non-const operator[],我们必须确保没有其他任何“共享同一个StringValue”的String对象因写动作而改变:

char& String::operator[](int index)
{
	//如果本对象和其他String对象共享同一实值
	//就分割(复制)出另一副本供本对象自己使用
	if(value->refCount > 1)
		--value->refCount;  //将目前实值的引用频次减1,因为我们不再使用
	
	//为自己做一份新的副本
	value = new StringValue(value->data);
	//返回一个reference,代表我们这个“绝对不被共享”的
	//StringValue对象内的一个字符。
	return value->data[index];
}

Pointers,References,以及Copy-On-Write

为每一个StringValue对象加上一个标志(flag)变量,用以指示可否被共享:

//修改版
class String
{
private:
	struct StringValue
	{
		int refCount;
		bool shareable;			//新增此行
		char* data;
	};
	StringValue(const char* initValue);
	~StringValue();
	...
};
 
String::StringValue::StringValue(const char* initValue)
		:refCount(1),
		shareable(true)//新增此行
{
	data = new char[strlen(initValue) + 1];
	strcpy(data,initValue);
}
 
String::StringValue::~StringValue()
{
	delete [] data;
}

//复制函数的新行为
String::String(const String& rhs)
{
	if(rhs.value->shareable)
	{
		value = rhs.value;
		++value->refCount;
	}
	else
	{
		value = new StringValue(rhs.value->data);
	}
}

//Non-const operator[]是唯一将shareble设为false者:
char& String::operator[](int index)
{
	if(value->refCount > 1)
		--value->refCount;
	
	value = new StringValue(value->data);
	value->shareable = false;	//新增此行
	return value->data[index];
}

一个Reference-Counting(引用计数)基类

  • Reference-counting可用于字符串以外的场合;
  • 任何class如果其不同的对象可能拥有相同的值,都适用此技术。

产生一个base class RCObject,作为“reference-counted对象”之用。
任何class希望自动拥有reference counting能力,都必须继承自这个类:

//RCObject定义
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::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;
}

//StringValue继承自RCObject
class String
{
private:
	//StringValue的成员函数不再处理refCount字段,改由RCObject掌管
	struct StringValue: public RCObject
	{
		char* data;
		StringValue(const char* initValue);
		~StringValue();
	};
	...
};
 
String::StringValue::StringValue(const char* initValue)
{
	data = new char[strlen(initValue) + 1];
	stpcpy(data,initValue);
}
 
String::StringValue::~StringValue()
{
	delete [] data;
}

自动操作Reference Count(引用次数)

我们希望能够把这一系列调用动作移到一个可复用的class内,这么一来可以让诸如String之类的classes的作者不必操心reference counting的任何细节。

//下面这个模板用来产生智能指针指向reference-counted对象:
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();
};

//实现
template<T>
RCPtr<T>::RCPtr(T* realPtr = 0)
	:pointee(realPtr)
{
		init();
}
 
template<T>
RCPtr<T>::RCPtr(const RCPtr& rhs)
	pointee(rhs.pointee)
{
	init();
}
 
template<T>
void RCPtr<T>::init()
{
	if(pointee == NULL)  
		return ;		
	
	if(pointee->isShareble() == false)
		pointee = new T(*pointee);
	
	pointee->addReference();
}

以上错误在于:init!
pointee = new T(*pointee)构造新的T对象,但是RCPtr在String类内部,T将是String::StringValue,所以我们必须为String::StringValue加上深复制函数:

class String
{
private:
	struct StringValue: public RCObject
	{
		StringValue(const StringValue& rhs);
		...
	};
};
 
String::StringValue::StringValue(const StringValue& rhs)
{
	data = new char[strlen(rhs.data) + 1];
	strcpy(data,rhs.data);
}

把所有努力放在一起

template<class T>				//template class,用来产生
class RCPtr						//smart pointers-to-Tobjects;
{								//T必须继承自RCObject
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();
};
 
class RCObject				//base class, 用于reference-counted objects
{
public:
	void addReference();
	void removeReference();
	void markUnshanreable();
	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;
};
 
class String		//应用性class,这是应用程序开发人员接触的层面
{
public:
	String(const char* value = "");
	const char& operator[](int index) const;
	char& operator[](int index);
	
private:
	struct StringValue: public RCObject
	{
		char* data;
		StringValue(const char* initValue);
		StringValue(const StringValue& rhs);
		void init(const char* initValue);
		~StringValue();
	};
	RCPtr<StringValue> value;
};


//RCObject实现
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::markUnshanreable()
{ shareable = false; }
bool RCObject::isShareable() const
{
	return shareable;
}
bool RCObject::isShared() const
{
	return refCount > 1;
}

//RCPtr实现
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 = 0)
	:pointee(realPtr)
{ 
	init();
}
 
template<class T>	
RCPtr<T>::RCPtr(const RCPtr& rhs)
	:pointee(realPtr)
{
	init(); 
}
 
template<class T>
RCPtr<T>::~RCPtr()
{
	if(pointee)
		pointee->removeReference();
}
 
template<class T>
RCPtr& 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::StringValue实现
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::String(const char* value = "")
	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->markUnshanreable();
	return value->data[index];
}

将Reference Counting加到既有的Classes身上

就像“StringValue只能实现细节,不需让String的用户知道”一样;
CountHolder也是实现细节,不需让RCWidget的用户知道。
事实上它是RCIPtr的实现细节,所以嵌套放入RCIPtr class内部。

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(*pointee);
	}	
	pointee->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& RCIPtr<T>::operator = (const RCIPtr& rhs)
{
	if(counter != rhs.counter)
	{
		counter->removeReference();
		counter = rhs.counter;
		init();
	}
	return *this;
}
 
template<class T>
const T* RCIPtr<T>::operator->() const
{
	return counter->pointee;
}
 
template<class T>
const T& RCIPtr<T>::operator*() const
{
	return *(counter->pointee);
}

总结

什么时候最适合reference counting技术:

  1. 相对多数的对象共享相对少了的实值;
  2. 对象实值的产生或销毁成本很高,或使它们使用许多内存。

你可能感兴趣的:(C++进阶,c++,开发语言,笔记)