文章标题 boost指针的引用计数,以及引发的资源共享和boost指针的交叉问题

来源:互联网 发布:丽水学院网络教学平台 编辑:程序博客网 时间:2024/06/04 19:22
    我们平常使用的指针也叫裸指针,大家都知道申请内存时我们需要手动申请,但是有时候会因为使用完而忘记释放资源而引起内存上的泄漏。    那么如何能保证我们手动申请的资源不会发生这种情况?    boost指针,它可以让我们放心的去使用资源而不会发生内存泄漏问题。    其原理就是利用对象的生命周期,对裸指针进行封装达到目的。话不多说,上代码:
class CSmartPtr    {    public:        CSmartPtr(T *ptr = NULL):mptr(ptr)  {}        ~CSmartPtr()    {        delete mptr;        mptr = NULL;    }    private:        int *mptr;               };    int main()    {        CSmartPtr ptr1(new int);        return 0;    }
    资源虽然得到了释放但是又产生了新的问题。    多个boost指针同时指向一个资源,资源会被重复释放还是会挂掉。    我们可以试想一下,如果我们对同一资源进行多次引用时可以记录一下它被引用了多少次。    那么再释放时,我们只在计数为1时对资源进行释放就可以解决这个问题。
这里我们可以采用库里的map来保存资源以及相应的引用计数。为了更好体现封装,我们将map单独写一个类,并且提供了相应的引用计数操作方法。class CResMap    {    public:        void addRef(void *mptr)        {            map<void*,int>::iterator it = mResMap.find(mptr);            if(it != mResMap.end())            {                it->second++;                return;            }            //mResMap.insert(make_pair(mptr, 1));            mResMap[mptr] = 1;        }        void delRef(void *mptr)        {            map<void*, int>::iterator it = mResMap.find(mptr);            if(it != mResMap.end())            {                if(it->second-- == 0)                    mResMap.erase(it);                return;            }        }        int getRef(void *mptr)        {            map<void*, int>::iterator it = mResMap.find(mptr);            if(it != mResMap.end())            {                return it->second;            }            return 0;        }        static CResMap* getInstance()        {               static CResMap mres;            return &mres;        }    private:        CResMap(){}        map<void*,int> mResMap;    };    template<typename T>    class CSmartPtr    {    public:        CSmartPtr(T *ptr = NULL):mptr(ptr)        {            if(mptr != NULL)                addRef();        }        ~CSmartPtr()        {            delRef();            if(getRef() == 0)            {                delete mptr;                 mptr=NULL;            }        }    private:        T *mptr;        static CResMap *mResMap;        void addRef()        {            mResMap->addRef(mptr);        }        void delRef()        {            mResMap->delRef(mptr);        }        int getRef()        {            return mResMap->getRef(mptr);        }    };
    现在我们已经可以达到了可以对同一资源的正确释放。但是因为我们用的是模板类。    所以当实例化两个及以上的类时,两张表中同一资源和引用计数,那么此时俩类都有程序还是会挂掉。    int *p = new int;    CSmartPtr<int> ptr1(p);    CSmartPtr<char> ptr2((char*)p);    //结束的时候,还会挂在析构那里    解决办法其实已经写入代码中了(单例模式),就是让不同的类型也共享同一个表,这样就不会出现引用计数失效。
    最后一个问题,boost指针的交叉问题。上图给大家解释一下:

交叉

    显然当我们执行完程序时,发现析构函数没有被调用,也就是资源没有被释放。    两个资源里面的智能指针互相指向对方,这时候就产生问题(你不是释放,我不释放),导致双方的资源引用计数都是一,都无法释放掉。
    这里就引出了shared_ptr和weak_ptr。(主要避免循环引用导致内存泄漏)    使用weak_ptr避免shared_ptr的循环引用,weak_ptr在访问资源前必须先lock,提升为强指针才可以操作资源。    这里我们可以尝试简单模仿一下库里的办法。    template<typename T>    class CWeakPtr    {    public:        CWeakPtr(T *ptr = NULL):mptr(ptr){};        CWeakPtr(const CSmartPtr<T> &src)        {            mptr = src.mptr;        }        CWeakPtr& operator=(const CSmartPtr<T> &src)        {            mptr = src.mptr;            return *this;        }        CSmartPtr<T> lock()        {            return CSmartPtr<T>(CWeakPtr<T>(mptr));        }        friend class CSmartPtr<T>;    private:        T *mptr;    };    template<typename T>    CSmartPtr<T>::CSmartPtr(const CWeakPtr<T> &w):mptr(w.mptr)    {        if (getRef()>0)        {            addRef();        }    }
我们尝试提升一下,发现确实没有让引用计数增加。CSmartPtr<A> ptra(new A());cout<<ptra.use_count()<<endl;CWeakPtr<A> ptrb(ptra);cout<<ptra.use_count()<<endl;

“`

未提升

    这次我们提升一下看看,确实成功了。   

提升成功

    这样可以解决boost指针的交叉问题。其实以后可以大胆放心的去使用shared_ptr,因为它是线程安全的,是原子操作,不会发生同时加减的情况。
阅读全文
2 0