C++引用计数(reference counting)技术简介(3)
2016-02-02 19:12
495 查看
1.将Reference Counting加到既有的Class
要想将引用计数施加到现有的实值对象Widget上,按照前面讨论的,都需要修改Winget类的源代码。但是,有时程序库的内容不是我们呢可以修改的,又该如何做呢?如果令Widget继承自RCObject,我们必须增加一个RCWidget class给用户使用,这很像之前关于String/StringValue的讨论。RCWidget扮演String的角色,Widget扮演StringValue的角色。整个设计结构如下:
但这么做的话就需要修改Widget使其继承自RCObject。我们可以增加一个新的CountHolder class,用以持有引用计数,并令CountHolder继承自RCObject。我们也令CountHolder内含一个指针,指向一个Widget。然后将smart RCPtr template以同样聪明的RCIPtr template取代,RCIPtr template包含CountHolder的一个指针。RCIPtr 的”I”意指”indirect”间接。修改后的设计如下:
引用计数基类RCObject基本不变,其源码如下:
//引用计数基类 class RCObject{ public: RCObject();//构造函数 RCObject(const RCObject& rhs);//拷贝构造函数 RCObject& operator=(const RCObject& rhs);//拷贝赋值运算符 virtual ~RCObject() = 0;//析构函数 void addReference();//增加引用计数 void removeReference();//减少引用计数,如果变为0,销毁对象 void markUnshareable();//将可共享标志设为false bool isShareable() const;//判断其值是否可共享 bool isShared() const;//判断其值是否正在被共享 int getRefCount();//返回引用计数 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; }
template RCIPtr的实现如下:
//智能指针模板类,用来自动执行引用计数类成员的操控动作 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); }
RCIPtr和RCPtr之间有两个差异,一个是RCPtr对象直接指向实值,而RCIPtr对象通过中介层“CountHolder对象”指向实值。第二是RCIPtr将operator->和operator*重载了,如此一来只要有non-const access发生在被指物身上,copy-on-write(写时复制)就会被执行。
有了RCIPtr,RCWidget的实现就很容易,因为RCWidget的每一个函数都只是通过底层的RCIPtr转调用对应的Widget函数。Widget和RCWidget的示例代码如下。
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; }; 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; };
注意,RCWidget没有申明拷贝构造函数,也没有赋值运算符和析构函数,就像先前的String class一样,不再需要撰写这些函数了,因为编译器生成的默认版本做了正确的事情。
2.总结
引用计数的实现需要成本。每一个拥有计数能力的实值都有一个引用计数器,而大部分操作都需要能够以某种方式检查或处理这个引用计数器,因此对象的实值需要更多内存。而且引用计数的底层源代码比没有引用计数的复杂的多。引用计数是个优化计数,其适用前提是对象常常共享实值。使用引用计数改善效率的时机有以下两个:
第一,相对多数的对象共享相对少量的实值;
第二,对象实值的产生或销毁成本很高,或是它们使用许多内存。
参考文献
[1]More Effective C++.Scott Meyers著,侯捷译.P183-213.[2]http://blog.csdn.net/ruan875417/article/details/48267527.
相关文章推荐
- C++引用计数(reference counting)技术简介(3)
- 学习C++
- C/C++: Inline function, calloc vs malloc
- C/C++: Inline function, calloc vs malloc
- C/C++: Inline function, calloc vs malloc
- C/C++: Inline function, calloc vs malloc
- C/C++: Inline function, calloc vs malloc
- C/C++: Inline function, calloc vs malloc
- ||C||<基础>C语言基础知识框架结构//个人整理使用|仅供参考|
- 基础知识(十)C++常用函数.txt
- 逐梦C++之四:四种强制类型转换
- 第二次作业——C++学习
- 第二次作业——C++学习
- C语言堆栈入门——堆和栈的区别
- C++ struct 定义及初始化
- c++指针
- 函数库regexec匹配正则表达式
- protobuf 生成的C++代码详解
- can't load library 'libstdc++.so.6'
- [转载] C++ typedef 用法详解