智能指针--SharePtr ScopedPtr

 

在上一篇中介绍了auto_ptr的实现,我们说那是一种不太好的实现方法,其实主要就是在解决拷贝构造所带来的问题,

既然这样的话,有人就有了一种方法,既然拷贝会出现各种问题,那么我禁止拷贝!

scoped_ptr的实现就是一种防拷贝的机制。scoped(作用域的意思)

1.只声明不定义

2.声明为私有的,让在类外也没有办法定义

下面是模拟库里的scoped_ptr的实现如下代码:

 

#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;

template<class T>

class scoped_ptr
{
public:
    scoped_ptr(T * ptr):_ptr(ptr)
    {}
    ~scoped_ptr()
    {
        delete _ptr;
    }
    T operator*()
    {
        return *_ptr;
    }
    T *operator->()
    {
        return _ptr;
    }

private:
    T *_ptr;
    scoped_ptr(const scoped_ptr<T> & p1);//采用防拷贝
    scoped_ptr<T> &operator =(const scoped_ptr<T> & p1);
};

void test()
{
    scoped_ptr<string> p1(new string("hello"));
    cout<<*p1<<endl;

    scoped_ptr<string> p2(new string("world"));

    p2=p1;
    scoped_ptr<string> p3(p1);

}

int main()
{

    test();
    return 0;
}

 

 

ScopedPtr也是一种实现起来比较简单的方法

那么处理拷贝构造的问题和赋值运算符重载的问题,在之前我们说可以采用一种引用计数的方法,share_ptr就是这样实现的

下面来模拟库里面的share_ptr实现代码如下:

#include <stdio.h>
#include <string>
using namespace std;


template<class T>
class SharePtr
{
public:
    SharePtr(T * ptr):_ptr(ptr),_pCount(new int(1))
    {
    }
    ~SharePtr()
    {
        if(--(*_pCount)==0)
        {
            delete _ptr;
            delete _pCount;
        }
    }

    SharePtr(const SharePtr<T> &p1)
    {
        _ptr=p1._ptr;
        _pCount=p1._pCount;
        ++*_pCount;
    }

    SharePtr<T> &operator=(const SharePtr<T> & p1)
    {
        if(_ptr!=p1._ptr)
        {
            if(--(*_pCount)==0)
            {
                delete _ptr;
                delete _pCount;
            }
            _ptr=p1._ptr;
            _pCount=p1._pCount;
            ++*_pCount;
        }
        return * this;
    }

    T operator*()
    {
        return *_ptr;
    }
    T * operator->()
    {
        return _ptr;
    }

private:
    T *_ptr;
    int *_pCount;
};

void test()
{
    SharePtr<string> p1(new string("hello"));

    SharePtr<string> p2(new string("world"));

    cout<<*p1<<endl;
    cout<<*p2<<endl;

    p2=p1;
    cout<<*p2<<endl;

    SharePtr<string> p3(p1);
    cout<<*p3<<endl;
}

int main()
{
    test();
    return 0;
}

 

这里的实现与之前实现的string 很相似。

但是这里又会有新的问题出现

#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;


template<class T>
class SharePtr
{
public:
    SharePtr(T * ptr):_ptr(ptr),_pCount(new int(1))
    {
    }
    ~SharePtr()
    {
        if(--(*_pCount)==0)
        {
            delete _ptr;
            delete _pCount;
            cout<<"~SharePtr()"<<endl;
        }
    }

    SharePtr(const SharePtr<T> &p1)
    {
        _ptr=p1._ptr;
        _pCount=p1._pCount;
        ++*_pCount;
    }

    SharePtr<T> &operator=(const SharePtr<T> & p1)
    {
        if(_ptr!=p1._ptr)
        {
            if(--(*_pCount)==0)
            {
                delete _ptr;
                delete _pCount;
            }
            _ptr=p1._ptr;
            _pCount=p1._pCount;
            ++*_pCount;
        }
        return * this;
    }

    T operator*()
    {
        return *_ptr;
    }
    T * operator->()
    {
        return _ptr;
    }
    int GetCount()
    {
        return *_pCount;
    }

private:
    T *_ptr;
    int *_pCount;
};


struct ListNode
{
    SharePtr<ListNode> _next;
    SharePtr<ListNode> _prev;
    int _data; 

    ListNode(int val=0):_next(NULL),_prev(NULL),_data(val)
    {}
};


void test()
{
   // ListNode *node1=new ListNode(1);
   // ListNode *node2=new ListNode(2);
   // //因为中间可能出现异常
   // //所以需要用智能指针来管理
   // delete node1;
   // delete node2;
    
    SharePtr<ListNode> node1(new ListNode(1));
    SharePtr<ListNode> node2(new ListNode(2));

    cout<<"node1.count:"<<node1.GetCount()<<endl;
    cout<<"node2.count:"<<node2.GetCount()<<endl;

    node1->_next=node2;
    node2->_next=node1;

    cout<<"node1.count:"<<node1.GetCount()<<endl;
    cout<<"node2.count:"<<node2.GetCount()<<endl;
    
}

int main()
{
    test();
    return 0;
}

 

我们看到,当节点里面的next和prev的指向改变时,会改变node1和node2的引用计数,即使程序结束,也最终只是将引用计数减一,并没有释放节点,造成这种问题,我们称为循环引用。

 

 

造成这种问题的原因是,在修改next和prev指向时 不能修改引用计数

 

 

这里提出了一个辅助share_ptr的weak_ptr,(weak_ptr接收的是share_ptr,只是处理时并不处理引用计数)

模拟实现如下:

#include <stdio.h>
#include <string>
using namespace std;

template<class T>
class SharePtr;

template<class T>
class WeakPtr
{
public:
    WeakPtr(const SharePtr<T> p1):_ptr(p1._ptr)
    {}

    T &operator*()
    {
        return * _ptr;
    }

    T* operator->()
    {
        return _ptr;
    }
private:
    T *_ptr;

};


template<class T>
class SharePtr
{
    friend class WeakPtr<T>;//因为WeakPtr中要用到其私有成员,将WeakPtr声明为其友元类
public:
    SharePtr(T * ptr):_ptr(ptr),_pCount(new int(1))
    {
    }
    ~SharePtr()
    {
        if(--(*_pCount)==0)
        {
            if(_ptr)
            {
                delete _ptr;
                cout<<"~SharePtr()"<<endl;
            }
            delete _pCount;
        }
    }

    SharePtr(const SharePtr<T> &p1)
    {
        _ptr=p1._ptr;
        _pCount=p1._pCount;
        ++*_pCount;
    }

    SharePtr<T> &operator=(const SharePtr<T> & p1)
    {
        if(_ptr!=p1._ptr)
        {
            if(--(*_pCount)==0)
            {
                delete _ptr;
                delete _pCount;
            }
            _ptr=p1._ptr;
            _pCount=p1._pCount;
            ++*_pCount;
        }
        return * this;
    }

    T operator*()
    {
        return *_ptr;
    }
    T * operator->()
    {
        return _ptr;
    }
    int GetCount()
    {
        return *_pCount;
    }

private:
    T *_ptr;
    int *_pCount;
};


struct ListNode
{
    WeakPtr<ListNode> _next;
    WeakPtr<ListNode> _prev;
    int _data; 

    ListNode(int val=0):_next(NULL),_prev(NULL),_data(val)
    {}
};


void test()
{
   // ListNode *node1=new ListNode(1);
   // ListNode *node2=new ListNode(2);
   // //因为中间可能出现异常
   // //所以需要用智能指针来管理
   // delete node1;
   // delete node2;
    
    SharePtr<ListNode> node1(new ListNode(1));
    SharePtr<ListNode> node2(new ListNode(2));

    node1->_next=node2;
    node2->_next=node1;

    cout<<"node1.count:"<<node1.GetCount()<<endl;
    cout<<"node2.count:"<<node2.GetCount()<<endl;
    
}

int main()
{
    test();
    return 0;
}

 

如上,就可以解决循环引用的问题。

注意:我们这里的WeakPtr是里面没有_pCount成员(引用计数),但是库里面是有的,库里面的share_ptr是借助了6个类来实现的。weak_ptr中的引用计数和share_ptr中的引用计数是一致的,share_ptr中的引用计数加加时,weak_ptr中的引用计数也要进行加加。它的引用计数是为了看自己的指针是否有效,我了解的也不是很多,大家可以自己下去研究。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智能指针share_ptr是C++11标准库中的一种用于管理动态分配内存的智能指针。它能够自动管理资源,在不需要时释放内存,防止内存泄漏。然而,在实际代码中,我们常常会遇到相互引用的情况,即两个或多个对象之间产生循环引用,导致内存泄漏。这种情况对于传统的裸指针来说是无法解决的,但对于智能指针share_ptr来说,则可以通过以下方法解决: 1.使用weak_ptr:在循环引用的情况下,一个对象可以通过weak_ptr来共享相应的资源,而不会增加资源的引用计数。因此,在处理相互引用问题时,可以使用weak_ptr来打破循环引用,避免内存泄漏。 2.使用单独的管理对象:在某些情况下,多个对象之间的相互引用较为复杂,使用weak_ptr不能完全解决问题,例如多层嵌套的相互引用。这时候,可以引入一个额外的管理对象,用于管理这些对象之间的关系,从而减少相互引用的复杂度。 3.使用裸指针代替智能指针:虽然智能指针share_ptr可以很好地管理内存,但在某些场景下,使用裸指针可能会更为简单、清晰。例如,在处理循环引用时,可以使用裸指针进行判断,然后手动删除内存。 综上所述,智能指针share_ptr是C++中非常有用的工具,在避免内存泄漏方面发挥了很大的作用。虽然在处理相互引用时可能会遇到一些麻烦,但使用上述方法可以很好地解决问题,保证代码质量和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值