【more effective c++读书笔记】【第5章】技术(5)——Reference counting(引用计数)(2)

三、引用计数基类和智能指针实现的String类

//RCObject.h
#ifndef RCOBJECT_H
#define RCOBJECT
//引用计数基类
class RCObject{
public:
	void addReference();//增加引用计数
	void removeReference();//减少引用计数,如果变为0,销毁对象
	void markUnshareable();//将追踪其值是否可共享的成员设为false
	bool isShareable() const;//判断其值是否可共享
	bool isShared() const;//判断其值是否正在被共享
	int getRefCount();//返回引用计数
protected:
	RCObject();//构造函数
	RCObject(const RCObject& rhs);//拷贝构造函数
	RCObject& operator=(const RCObject& rhs);//拷贝赋值运算符
	virtual ~RCObject() = 0;//析构函数
private:
	int refCount;//保存引用计数
	bool shareable;//保存其值是否可共享的状态
};
//构造函数,这里refCount设为0,让对象创建者自行或将refCoun设为1
RCObject::RCObject(void) :refCount(0), shareable(true){}
//拷贝构造函数,总是将refCount设为0,因为正在产生一个新对象,只被创建者引用
RCObject::RCObject(const RCObject&) : refCount(0), shareable(true){}
//拷贝赋值运算符,这里只返回*this,因为左右两方RCObject对象的外围对象个数不受影响
RCObject& RCObject::operator=(const RCObject& rhs){
	return *this;
}
//析构函数
RCObject::~RCObject(){}
//增加引用计数
void RCObject::addReference(){
	++refCount;
}
//减少引用计数,如果变为0,销毁对象
void RCObject::removeReference(){
	if (--refCount == 0)
		delete this;
}
//将追踪其值是否可共享的成员设为false
void RCObject::markUnshareable(){
	shareable = false;
}
//判断其值是否可共享
bool RCObject::isShareable() const{
	return shareable;
}
//判断其值是否正在被共享
bool RCObject::isShared() const{
	return refCount>1;
}
//返回引用计数
int RCObject::getRefCount(){
	return refCount;
}

#endif
//RCPtr.h
#ifndef RCPTR_H
#define RCPTR_H
//智能指针模板类,用来自动执行引用计数类成员的操控动作
template<typename 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<typename T>
void RCPtr<T>::init(){
	if (pointee == 0) return;
	if (pointee->isShareable() == false) {
		pointee = new T(*pointee);
	}
	pointee->addReference();
}
//构造函数
template<typename T>
RCPtr<T>::RCPtr(T* realPtr) :pointee(realPtr){
	init();
}
//拷贝构造函数
template<typename T>
RCPtr<T>::RCPtr(const RCPtr& rhs) : pointee(rhs.pointee){
	init();
}
//析构函数
template<typename T>
RCPtr<T>::~RCPtr(){
	if (pointee)
		pointee->removeReference();
}
//拷贝赋值运算符
template<typename T>
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs){
	if (pointee != rhs.pointee) {
		if (pointee)
			pointee->removeReference();
		pointee = rhs.pointee;
		init();
	}
	return *this;
}
//重载->运算符
template<typename T>
T* RCPtr<T>::operator->() const { return pointee; }
//重载*运算符
template<typename T>
T& RCPtr<T>::operator*() const { return *pointee; }

#endif
//String.h
#ifndef STRING_H
#define STRING_H

#define _CRT_SECURE_NO_WARNINGS
#include"RCObject.h"
#include"RCPtr.h"
#include<iostream>

class String {                           
public:                                
	String(const char *value = "");//构造函数
	const char& operator[](int index) const;//重载[]运算符,针对const Strings
	char& operator[](int index);//重载[]运算符,针对non-const Strings
	int getRefCount();//返回引用计数
	friend std::istream& operator>>(std::istream& is, const String& str);//重载>>运算符
	friend std::ostream& operator<<(std::ostream& os, const String& str);//重载<<运算符
private:
	struct StringValue : public RCObject {//继承自引用计数基类
		char *data;
		StringValue(const char *initValue);//构造函数
		StringValue(const StringValue& rhs);//拷贝赋值运算符
		void init(const char *initValue);
		~StringValue();//析构函数
	};
	RCPtr<StringValue> value;//智能指针对象
};
//String::StringValue实现代码
void String::StringValue::init(const char *initValue){
	data = new char[strlen(initValue) + 1];
	strcpy(data, initValue);
}
//StringValue类的构造函数
String::StringValue::StringValue(const char *initValue){
	init(initValue);
}
//StringValue类的拷贝赋值运算符
String::StringValue::StringValue(const StringValue& rhs){
	init(rhs.data);
}
//StringValue类的析构函数
String::StringValue::~StringValue(){
	delete[] data;
}
//String实现代码
//String类的构造函数
String::String(const char *initValue)
: value(new StringValue(initValue)) {}
//重载[]运算符,针对const Strings
const char& String::operator[](int index) const{
	return value->data[index];
}
//重载[]运算符,针对non-const Strings
char& String::operator[](int index){
	if (value->isShared()) {
		value = new StringValue(value->data);
	}
	value->markUnshareable();
	return value->data[index];
}
//返回引用计数
int String::getRefCount(){
	return value->getRefCount();
}
//重载>>运算符
std::istream& operator>>(std::istream& is, const String& str){
	is >> str.value->data;
	return is;
}
//重载<<运算符
std::ostream& operator<<(std::ostream& os, const String& str){
	os << str.value->data;
	return os;
}

#endif
//main.cpp
#include"String.h"
#include<iostream>
using namespace std;

int main(){
	String str1("hello world");
	String str2 = str1;//调用拷贝构造函数
	String str3;//调用默认构造函数
	str3 = str2;//调用拷贝赋值运算符
	cout << "str1的引用计数是:" << str1.getRefCount() << endl; // 3
	cout << "str2的引用计数是:" << str2.getRefCount() << endl; // 3
	cout << "str3的引用计数是:" << str3.getRefCount() << endl; // 3

	str1[0] = 'H';//调用针对non-const Strings的重载[]运算符
	cout << str1 << endl; //"Hello world"
	cout << str2 << endl;//"hello world"
	cout << str3 << endl;//"hello world" 
	cout << "str1的引用计数是:" << str1.getRefCount() << endl;//1
	cout << "str2的引用计数是:" << str2.getRefCount() << endl;//2
	cout << "str3的引用计数是:" << str3.getRefCount() << endl;//2

	system("pause");
	return 0;
}

上述实现的String类的数据结构如下:

和上一个用dumbpointers实现的String类比较:第一,这个版本精简了许多,因为RCPtr类做掉了许多原本落在String身上的引用计数杂务;第二,智能指针几乎毫无间隙地取代了dumb pointer。

四、将引用计数加到既有的类身上

//RCObject.h
#ifndef RCOBJECT_H
#define RCOBJECT
//引用计数基类
class RCObject{
public:
	void addReference();//增加引用计数
	void removeReference();//减少引用计数,如果变为0,销毁对象
	void markUnshareable();//将追踪其值是否可共享的成员设为false
	bool isShareable() const;//判断其值是否可共享
	bool isShared() const;//判断其值是否正在被共享
	int getRefCount();//返回引用计数
protected:
	RCObject();//构造函数
	RCObject(const RCObject& rhs);//拷贝构造函数
	RCObject& operator=(const RCObject& rhs);//拷贝赋值运算符
	virtual ~RCObject() = 0;//析构函数
private:
	int refCount;//保存引用计数
	bool shareable;//保存其值是否可共享的状态
};
//构造函数,这里refCount设为0,让对象创建者自行或将refCoun设为1
RCObject::RCObject(void) :refCount(0), shareable(true){}
//拷贝构造函数,总是将refCount设为0,因为正在产生一个新对象,只被创建者引用
RCObject::RCObject(const RCObject&) : refCount(0), shareable(true){}
//拷贝赋值运算符,这里只返回*this,因为左右两方RCObject对象的外围对象个数不受影响
RCObject& RCObject::operator=(const RCObject& rhs){
	return *this;
}
//析构函数
RCObject::~RCObject(){}
//增加引用计数
void RCObject::addReference(){
	++refCount;
}
//减少引用计数,如果变为0,销毁对象
void RCObject::removeReference(){
	if (--refCount == 0)
		delete this;
}
//将追踪其值是否可共享的成员设为false
void RCObject::markUnshareable(){
	shareable = false;
}
//判断其值是否可共享
bool RCObject::isShareable() const{
	return shareable;
}
//判断其值是否正在被共享
bool RCObject::isShared() const{
	return refCount>1;
}
//返回引用计数
int RCObject::getRefCount(){
	return refCount;
}

#endif
//RCIPtr.h
#ifndef RCIPTR_H
#define RCIPTR_H

#include "RCObject.h"
//智能指针模板类,用来自动执行引用计数类成员的操控动作
template<typename 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();//copy-on-write中的copy部分
};
//共同的初始化操作
template <typename T>
void RCIPtr<T>::init(){
	if (counter->isShareable() == false){
		T* oldValue = counter->pointee;
		counter = new CountHolder;
		counter->pointee = new T(*oldValue);
	}
	counter->addReference();
}
//构造函数
template <typename T>
RCIPtr<T>::RCIPtr(T* realPtr) :counter(new CountHolder){
	counter->pointee = realPtr;
	init();
}
//拷贝构造函数
template <typename T>
RCIPtr<T>::RCIPtr(const RCIPtr& rhs) :counter(rhs.counter){
	init();
}
//析构函数
template <typename T>
RCIPtr<T>::~RCIPtr(){
	counter->removeReference();
}
//拷贝赋值运算符
template <typename T>
RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs){
	if (counter != rhs.counter){
		counter->removeReference();
		counter = rhs.counter;
		init();
	}
	return *this;
}
//重载->运算符,const版本
template<typename T>
const T* RCIPtr<T>::operator->() const { return counter->pointee; }
//重载*运算符,non-const版本
template<typename T>
const T& RCIPtr<T>::operator*() const { return *(counter->pointee); }
//copy-on-write中的copy部分
template <typename T>
void RCIPtr<T>::makeCopy(){
	if (counter->isShared()){
		T* oldValue = counter->pointee;
		counter->removeReference();
		counter = new CountHolder;
		counter->pointee = new T(*oldValue);
		counter->addReference();
	}
}
//重载->运算符,non-const版本
template <typename T>
T* RCIPtr<T>::operator->(){
	makeCopy();
	return counter->pointee;
}
//重载*运算符,non-const版本
template <typename T>
T& RCIPtr<T>::operator*(){
	makeCopy();
	return *(counter->pointee);
}

#endif
//Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <iostream>
class Widget{
public:
	Widget(int s = 0) :size(s){}
	Widget(const Widget& rhs) { size = rhs.size; }
	~Widget(void) {}

	Widget& operator=(const Widget& rhs){
		if (this == &rhs)
			return *this;
		this->size = rhs.size;
		return *this;
	}
	void doThis() { std::cout << "doThis()" << std::endl; }
	int showThat() const { 
		std::cout << "showThat()" << std::endl; 
		return size; 
	}
private:
	int size;
};

#endif
//RCWidget.h
#ifndef RCWIDGET_H
#define RCWIDGET_H

#include "RCIPtr.h"
#include "Widget.h"

class RCWidget{
public:
	RCWidget(int size = 0) :value(new Widget(size)){}
	~RCWidget() {}

	void doThis() { value->doThis(); }
	int showThat() const { return value->showThat(); }
private:
	RCIPtr<Widget> value;
};

#endif
//main.cpp
#include"RCWidget.h"
using namespace std;

int main(){
	RCWidget  rc1(5);
	rc1.doThis();
	cout << rc1.showThat() << endl;

	RCWidget  rc2(rc1);
	rc2.doThis();
	cout << rc2.showThat() << endl;

	system("pause");
	return 0;
}

上述例子的数据结构如下:


RCIRtr和RCPtr之间有两个差异:第一,RCPtr对象直接指向实值,而RCIPtr对象通过中间层CountHolder对象指向实值;第二,RCIPtr将operator->和operator*重载了,这样只要有non-const access发生于被指物身上,copy-on-write就会自动执行。

总结:引用计数的实现需要成本。每一个拥有计数能力的实值都有一个引用计数器,而大部分操作都需要能够以某种方式检查或处理这个引用计数器,因此对象的实值需要更多内存。而且引用计数的底层源代码比没有引用计数的复杂的多。

引用计数是个优化计数,其适用前提是对象常常共享实值。使用引用计数改善效率的时机有以下两个:第一,相对多数的对象共享相对少量的实值;第二,对象实值的产生或销毁成本很高,或是它们使用许多内存。


版权声明:本文为博主原创文章,未经博主允许不得转载。

原文地址:https://www.cnblogs.com/ruan875417/p/4921353.html