zoukankan      html  css  js  c++  java
  • 【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就会自动执行。

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

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


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

  • 相关阅读:
    javascript不用正则验证输入的字符串是否为空(包含空格)
    最近真的很忙
    加油吧 骚年QAQ
    发现一个问题:个人博客仅仅存在于有生之年,如何一直保存下去呢?
    day01-html
    day04-jQuery
    Day03-jS
    MySQL-注释-Navicat基本使用-复杂查询练习题-解题思路-pymysql操作数据库-SQL注入-05
    MySQL-复杂查询及条件-起别名-多表查询-04
    MySQ-表关系-外键-修改表结构-复制表-03
  • 原文地址:https://www.cnblogs.com/ruan875417/p/4921353.html
Copyright © 2011-2022 走看看