现在的位置: 首页 > 综合 > 正文

引用计数+智能指针的自析构类 (一)基本实现

2018年01月11日 ⁄ 综合 ⁄ 共 3108字 ⁄ 字号 评论关闭

  主要思路,对来值ref(refcount + 1),对去值deref(refcount - 1),这样不需手动做ref、deref操作就能合理的管理引用计数值。

  以下是more effective c++中智能指针+引用计数的实现,它的组成为:

  1. RCObject,引用计数类的基类,它封装了refcount +、- 操作ref()、deref()。无需其它额外操作。

  2. RCPtr<class T>,模板类,它实现引用计数+智能指针的核心操作。持有RCObject指针,调用构造函数后对RCObject的refCount + 1,析构时对其 - 1。注意拷贝构造函数和operator=操作符的实现,来实现来值ref,去值deref操作。

  3. 让类继承自RCObject,使用RCPtr<RCNormal>模板类型声明、创建对象。

//引用计数基类
class RCObject : public CBase
	{
public:
	RCObject() : refCount(0)/*, shareable(true)*/ {}
	virtual ~RCObject() {}
	/*重点处理拷贝构造和赋值操作
	 * 注意:两个操作都不要处理refCount值*/
	RCObject(const RCObject& aObj) : refCount(0)/*, shareable(true)*/ {}
	RCObject& operator=(const RCObject& aObj) { return *this; }
	
public:
	//refcount操作
	void ref() 
		{
		++refCount;
#if DEBUG
		LOGF8("refCount = [%d]", refCount);
#endif
		}
	
	void deref() 
		{
#if DEBUG
		LOGF8("refCount = [%d]", refCount - 1);
#endif
		if (--refCount == 0) 
			delete this;
		}
	
	int getRefCount() { return refCount; }
	
//	//share操作,可有可无
//	bool isShareable() { return shareable; }
//	bool isShared() { return refCount > 1; }
//	void markUnshareable() { shareable = false; }
	
private:
	int refCount;
//	bool shareable;
	
	};

//智能指针
template <class T> class RCPtr
	{
public:
	//缺省构造函数
	RCPtr(T* realPtr = 0) : pointee(realPtr) { init(); }
	virtual ~RCPtr() { if (pointee) pointee->deref(); }
	
	//拷贝构造
	RCPtr(const RCPtr& rhs) : pointee(rhs.pointee) 
		{
#if DEBUG
		_TRACE_;
#endif
		init();
		}
	
	//=操作符
	RCPtr& operator=(const RCPtr& rhs)
		{
#if DEBUG
		_TRACE_;
#endif
		//这个判断条件一定不能忘
		if (pointee != rhs.pointee)
			{
			if (pointee)
				pointee->deref();
			pointee = rhs.pointee;
			init();
			}
		return *this;
		}
	
public:
	//重载指针操作用到的操作符
	T* operator->() const { return pointee; }
	T& operator*() const { return *pointee; }
	
private:
	T* pointee;
	//比较关键
	void init() 
		{
		if (!pointee)
			return;
//   if (pointee->isShareable() == false)
//    pointee = new T(*pointee);
		pointee->ref();
		}
	
	};

  这里有几个值得注意的问题:

1. /* operator=的调用*/
RCPtr<Normal> objNull;
objNull = new Normal();

//以上调用产生LOG如下
//1. 先调用构造函数RCPtr(T* realPtr = 0),创建临时RCPtr<Normal>对象,refCount + 1
2012/02/13
08:43:58 refCount = [1]
//2. 调用operator=将临时对象,赋值给objNull
2012/02/13
08:43:58 RCPtr<Normal>::operator=(const RCPtr<Normal> &)
//3. refCount + 1
2012/02/13
08:43:58 refCount = [2]
//4. operator=调用完成
2012/02/13
08:43:58 END
//5. 析构临时对象refCount - 1
2012/02/13
08:43:58 refCount = [1]
可以看到这里因为临时对象的关系,reCount值有一次“跳跃”

2. 拷贝构造函数的调用
RCPtr<Normal> objNor(new Normal);
RCPtr<Normal> objNor1(objNor);

//以上调用产生LOG如下
//1. 先构造objNor对象,refCount + 1
2012/02/13
08:56:00 refCount = [1]
//2. 调用拷贝构造函数,创建objNor1对象
2012/02/13
08:56:00 RCPtr<Normal>::RCPtr(const RCPtr<Normal> &)
//3. refCount + 1
2012/02/13
08:56:00 refCount = [2]
//4. 拷贝构造函数调用完成
2012/02/13
08:56:00 END
//5. 析构临时对象refCount - 1
2012/02/13
08:56:00 refCount = [1]
2012/02/13
08:56:00 refCount = [0]

3. 做为函数参数值传递的情况

void passPtr(RCPtr<Normal> aNor)
{
aNor->getRefCount();
return;
}

RCPtr<Normal> b(new Normal);
/* 这里做了拷贝构造,把b拷贝给函数活动记录中的一个临时对象 */
passPtr(b);

4. 做为函数返回值的情况

RCPtr<Normal> createSpecialNode()
{
//正好检查一下RVO----直接优化掉了
RCPtr<Normal> a = new Normal;
a->getRefCount();
    return a;
}

RCPtr<Normal> b = createSpecialNode();

  理论上这里应该有很多因为临时对象而产生的操作的,但是因为目前很多编译器做了很好的编译器优化,这个问题就不是太突显了。

  上面的代码在Symbian C++编译器下面试了下,优化的程度令我吃惊。。。上面各种调用,被编译优化为:

  只调用一个拷贝构造函数!!!!实在不得了。

总结

  经过几种情况的分析,我们应该注意使用  引用计数+智能指针中存在的问题。

  1. 存在临时对象构造、析构的开销

  2. 由于1,同时存在refCount“跳跃”问题

  使用中应该一些原则一定要记清楚,以正确使用该技术:

  1. 引用计数类一定要有创建的原始对象

  2. RCObject类的拷贝、operator=不会做任何修改refcount的操作,这些操作都是由RCPtr智能指针完成

  3. 注意临时对象的存在

  下面一章讲解一下,Webkit中对这个技术点的使用,它做了怎么样的方案来解决这个问题的?

抱歉!评论已关闭.