实战智能指针(Smart Pointer)

(本站文章均出自原创,转载请注明出处~)


    最近在阅读Gamebryo的文档,先读的Object System这部分。Gamebryo中从NiRefObject继承下来的类都支持引用计数,也就是说支持0引用下对象自动销毁。
一直以来都听闻智能指针的大名,平常项目中并没有使用过,所以也一直没抽空去研究,今天花了点时间自己实现了一个智能指针。
    要实现一个智能指针就要先实现一个支持引用计数的类,主要负责增加计数,减少计数,销毁持有的对象指针。大致实现如下:

 

ContractedBlock.gif ExpandedBlockStart.gif class RefObject
template < class  T >   class  RefObject
{
public :
    
explicit  RefObject(T *  p):m_RefCount( 1 ),m_pContainer(p)
    {
    }
    
~ RefObject()
    {
        decRef();
    }
    
void  incRef()
    {
        
++ m_RefCount;
    }
    
void  decRef()
    {
        
if ( -- m_RefCount  ==   0 )
        {
            destroy();
        }
    }
    T
*  getPtr()
    {
        
return  m_pContainer;
    }
private :  
    
void  destroy()
    {
        delete m_pContainer;
        m_pContainer 
=   0 ;
        m_RefCount 
=   0 ;
        delete 
this ;
    }

private :
    T
*            m_pContainer;
    unsigned 
int  m_RefCount;

private :
    RefObject(
const  RefObject & );
    
const  RefObject &   operator =  ( const  RefObject & );
};

 

该模板类将持有一个使用者指定的对象T类型的指针,并维护该指针被引用的数量。在destroy函数中销毁T类型指针和自己。为什么要删除自己呢?这是因为RefObject将和T共存亡。因为我希望用户可以将任意类放入智能指针内部,而不是从RefObject继承下来。所以RefObject是为T而生(由SmartPtr new出来的)。当用户直接使用RefObject时需要自己去调用incRef和decRef去增加和减少计数,因此即将设计的类SmartPtr实际上就是封装下RefObject,不需要用户来调这两个函数。SmartPtr实现大致如下: 
ContractedBlock.gifclass SmartPtr
template < class  T >   class  SmartPtr
{
public :
    SmartPtr():m_pRefObject(
0 )
    {
    }
    
explicit  SmartPtr(T *  p)
    {
        m_pRefObject 
=   new  RefObject < T > (p);
    }
    SmartPtr(
const  SmartPtr &  other):m_pRefObject(other.m_pRefObject)
    {
        m_pRefObject
-> incRef();
    }
    
~ SmartPtr()
    {
        
if (m_pRefObject)
            m_pRefObject
-> decRef();
    }
    
const  SmartPtr < T >&   ff;">operator =  ( const  SmartPtr &  other)
    {
        
if (m_pRefObject  !=  other.m_pRefObject)
        {
            m_pRefObject 
=  other.m_pRefObject;
            m_pRefObject
-> incRef();
        }
        
return   * this ;
    }
    T
*   operator ->  ()  const
    {
        
return  m_pRefObject -> getPtr();
    }
    T
&   operator *  ()  const
    {
        
return   * m_pRefObject -> getPtr();
    }
protected :
    RefObject
< T >*  m_pRefObject;
};

 

 SmartPtr类在hold住一个T类指针时就new出一个RefObject与之相对应,重载赋值操作符来实现每赋值一次增加引用计数,析构函数中减少引用计数。并且重载->操作符以使SmartPtr使用起来像一个真正的指针。

    一切都非常简单,不是吗?当然这两个类实现的并不完整,有很多细小的功能没有提供,但大体脉络是抓住了。

 

    下面看看如何使用吧:

 

     int  main()
    {
        {
            SmartPtr
< CSample >  spSam( new  CSample());
            spSame
-> DoSomething();
        }
    }

 完全不用delete,实现垃圾自动回收效果,很酷吧。 哈哈,我初次写完这些代码是也觉的很爽,终于体验到了使用Java,C#的感觉了。可是智能指针还有一个致命的问题,这个问题就像是一盘美食中的苍蝇,让你心头非常不爽,并且对智能指针的好感立刻全无。哈哈哈,那就是:循环引用。当你写的类中不幸出现循环引用,那么你的智能指针将

不在智能,内存泄露随之产生,违背了智能指针的初衷。

 struct A

{
    SmartPtr
< B >  b;
}
struct  B
{
    SmartPtr
< A >  a;
}

int  main()
{
    SmartPtr
< A >  aa( new  A());
    SmartPtr
< B >  bb( new  B());

    aa.b 
=  bb;
    bb.a 
=  aa;
}

 

 

 如果你写了以上程序(虽然不常见,因为这种设计是非常糟糕的)你的aa和bb将无法释放!!!导致无法释放的原因是什么?这是因为aa获取了一次bb的所有权,但aa销毁时并没有释放这次获得的所有权,当把bb赋给aa的b时,bb的引用计数变为2,当bb销毁时bb所持有的对象引用减为1,而并不是0,所有他并不会释放他所持有的实际指针。其实

这种情况跟你写如下代码一样:

 class C

 

{
    A
*  a;
    C()
    {
        a 
=   new  A();
    }
}

 

类C在构造函数里new了一个A,却并没有在析构函数里delete它!

 

    我的解决方法是设计一个新类:SmartPtrHolder,此类用来存放一个SmartPtr,但并不增加计数。一种比较时髦的解释叫:弱引用。实现大致如下:

 

template<class T> class SmartPtrHoder
{
public :
    SmartPtrHoder():m_pRefObject(
0 )
    {
    }
    SmartPtrHoder(
const  SmartPtr < T >&  sp):m_pRefObject(sp.m_pRefObject)
    {
    }

    SmartPtrHoder
&   operator =  ( const  SmartPtr < T >&  sp)
    {
        
if (m_pRefObject  !=  sp.m_pRefObject)
        {
            m_pRefObject 
=  sp.m_pRefObject;
        }

        
return   * this ;
    }
    SmartPtr
< T >  use()
    {
        
return  SmartPtr < T > ( * this );
    }
protected :
    RefObject
< T >*  m_pRefObject;
};

 

这次重载的赋值操作符中并不增加SmartPtr的引用计数。当你需要使用这个SmartPtr时需要调用use()函数来把SmartPtr从SmartPtrHolder中取出来。

另外需要在SmartPtr中增加一个构造函数以支持从SmartPtrHoder中构造出一个SmartPtr。代码如下:

     explicit  SmartPtr(SmartPtrHoder < T >&  spHolder):m_pRefObject(spHolder.m_pRefObject)
    {
        m_pRefObject
-> incRef();
    }

 

 

这样再次设计A和B时: 

 

 struct A

 

{
    SmartPtrHolder
< B >  b;
}
struct  B
{
    SmartPtrHolder
< A >  a;
}

 

 

 

  这样就不会出现循环引用了。此时我想起了一位大师的话,翻译成中文大致如下:你现在有一个问题,你会说:“嗯~ 我准备用正则表达式来解决这个问题”。现在你有两个问题!

    
  使用智能指针本身就是一个问题。所以还是谨慎为好! 1739596.html?type=1

 

转载于:https://www.cnblogs.com/jisujun/archive/2010/05/19/1776899.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值