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

来源:互联网 发布:mysql两个表的添加语句 编辑:程序博客网 时间:2024/06/13 02:02

  主要思路,对来值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 DEBUGLOGF8("refCount = [%d]", refCount);#endif}void deref() {#if DEBUGLOGF8("refCount = [%d]", refCount - 1);#endifif (--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_;#endifinit();}//=操作符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中对这个技术点的使用,它做了怎么样的方案来解决这个问题的?