智能指针的用法

产生原由:当你在堆中分配出一块内存,即使用new或者malloc出来的空间,也需要自己手动去调用delete或者free去析构这块空间,但是忘记析构的时候,会产生内存泄漏等问题,此时产生了智能指针来解决此类问题。
智能指针类型:auto_ptr,scoped_ptr,shared_ptr,weak_ptr
1.auto_ptr
属于STL库,包含在#include<memory>头文件中,用来管理单个堆内存,简单使用但是有许多缺陷
实现auto_ptr
(1)权限转移

template<class T>

class My_autoptr
{
public:
    My_autoptr(T*ptr=0)
        :_ptr(ptr)
    {
        ptr = NULL;
    }
    My_autoptr(My_autoptr<T> & ap)
        :_ptr(ap._ptr)
    {
        ap._ptr = NULL;
    }
    My_autoptr<T>& operator =(My_autoptr<T>& ap)
    {
        if (this != &ap)
        {
            if (_ptr)
            {
                delete _ptr;
            }
            _ptr = ap._ptr;
            ap._ptr = NULL;
        }
        return *this;
    }
    ~My_autoptr()
    {
        if (_ptr)
        {
            delete _ptr;
            _ptr = NULL;
        }
    }
    T &operator*()
    {
        return *_ptr;
    }
    T *operator->()
    {
        return _ptr;
    }
private:
    T *_ptr;
};
void test()
{
    My_autoptr<int>p(new int(123));
    My_autoptr<int>p1(p);
    My_autoptr<int>p2;
    p2 = p1;
}

如图所示:
这里写图片描述
(2)在AutoPtr的类内加入新的成员变量bool类型的_owner,构造函数中将_owner设置为true,表示对象是指针所指向内存的拥有者。
代码实现

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* ptr=0)
        :_ptr(ptr)
        , _owner(true)
    {
        ptr = NULL;
    }
    AutoPtr(AutoPtr<T>& ap)
        :_ptr(ap._ptr)
        , _owner(true)
    {
        ap._owner=false;
    }
    ~AutoPtr()
    {
        if (_owner)
        {
            delete _ptr;
        }
    }
    AutoPtr<T>&operator=(AutoPtr<T>& ap)
    {
        if (this != &ap)
        {
            if (_owner){
                delete _ptr;
            }
            _ptr = ap._ptr;
            _owner = true;
            ap._owner = false;
        }
        return *this;
    }
    T* operator->()
    {
        return _ptr;
    }
    T &operator*()
    {
        return *_ptr;
    }
private:
    T *_ptr;
    bool _owner;
};
void test()
{
    AutoPtr<int>p(new int(1));
    if(_owner)
    {
        AutoPtr<int>p1(p);//此时p1为true,p为false
    }
    *p=2;//当p1作为临时变量退出时调用析构函数,此时p变成了野指针,*p便会报错
}

这里写图片描述

get()返回当前指针对象
operator*返回指针所指向的内容
operator->返回成员对象
release()清空当前智能指针,返回指针类型
operator=清空当前智能指针并拷贝给新的智能指针
reset()清空当前智能指针,将指针置为空。

2.scoped_ptr
属于boost库,一个类似于auto_ptr,不过scoped_ptr独享所有权,不能进行赋值与拷贝!!!在这里我们只是将赋值运算符重载和operator=重载进行声明不进行定义,因此此智能指针功能不全。
代码实现:

template<class T>
class ScopedPtr
{
public:
    ScopedPtr(const T*ptr = 0)
        :_ptr(ptr)
    {
        ptr = NULL;
    }
    ~ScopedPtr()
    {
        if (_ptr)
            delete _ptr;
    }
    T*operator ->()
    {
        return _ptr;
    }
    T& operator*()
    {
        return *_ptr;
    }
    T* get()
    {
        return _ptr;
    }
    void reset(T*p=0)
    {
        if(_ptr!=p)
        {
        delete _ptr;
        _ptr=p;
        }
    }
//防拷贝
//1.只声明不实现
//2.声明成私有保护
privateScopedPtr(const ScopedPtr<T>&);
    ScopedPtr<T>& operator=(const ScopedPtr<T>&);

protected:
    T *_ptr;
};

通过代码我们可以看出并没有实现operator=以及拷贝构造函数,便不会导致所有权转移等问题


3.shared_ptr
属于boost库,包含头文件#include<boost/smart_ptr.hpp>,用于管理单个堆内存对象,shared_ptr做到了共享所有权,同时内部使用引用计数的方式,_refcount初始构造为1,当_refcount==0时,调用析构函数。
shared_ptr最安全的分配和使用动态内存的方式是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr
例如:

shared_ptr<int>p1=make_shared<int>(42)//指向一个值为42的int型的shared_ptr
shared_ptr<string>p2=make_shared<string>(10,'9');//指向一个值为"9999999999"的string

首先来说明shared_ptr存在的循环引用的问题

#include<boost/shared_ptr.hpp>
#include<boost/weak_ptr.hpp>
using namespace boost;

struct ListNode
{
    shared_ptr<ListNode> _prev;
    shared_ptr<ListNode> _next;
    ~ListNode()
    {
        cout<<"~ListNode()"<<endl;
    }
};
void test()
{
    shared_ptr<ListNode>p1(new ListNode());
    shared_ptr<ListNode>p2(new ListNode());
    cout<<"p1->count:"<<p1.use_count()<<endl;
    cout<<"p2->count:"<<p2.use_count()<<endl;

    p1->_next=p2;
    p2->_prev=p1;

    cout<<"p1->count:"<<p1.use_count()<<endl;
    cout<<"p2->count:"<<p2.use_count()<<endl;
}

当你实现此段代码后,你将会发现p1与p2的计数器都会是2;当你选择析构其中一个指针的时候便会依赖与另外一个,此时便会产生一个循环,且两者都不会进行析构,造成内存泄漏。
如何能正常解决循环引用的问题,此时我们便加入weak_ptr弱引用智能指针。
4.weak_ptr
weak_ptr指向shared_ptr指向对象的内存,但不共享这块内存,也没有重载operator*和operator->,他的构造不会使指针引用计数的增加。

weak_ptr w
use_count():可以观测shared_ptr的引用计数;
expired():若与之对应的use_count()为0,返回true,否则返回false;
lock():若expired为true,返回一个空shared_ptr;否则返回一个指向w的对象的shared_ptr;

如何通过weak_ptr来解决shared_ptr的循环引用问题
代码实现:

struct Delete//由于shared_ptr存在的循环引用导致指针不能被自动析构,此时我们定制删除器
{
    template<class T>
    void operator()(T* ptr)
    {
        delete ptr;
    }
};

template<class T>
class WeakPtr;
template<class T, class D= Delete>
class SharedPtr
{
    friend class WeakPtr<T>;
public:
    SharedPtr(T*ptr = 0,D del= Delete())
        :_ptr(ptr)
        , _refCount(new int(1))
        ,_del(del)
    {}
    SharedPtr(const SharedPtr<T, D>& sp)
        :_ptr(sp._ptr)
        ,_refCount(sp._refCount)
    {
        ++(*_refCount);
    }

    SharedPtr<T, D>& operator=(const SharedPtr<T, D>& sp)
    {
        if (_ptr != sp._ptr)
        {
            Release();
            _ptr = sp._ptr;
            _refCount = sp._refCount;
            ++(*_refCount);
        }

        return *this;
    }

    ~SharedPtr()
    {
        Release();
    }
    T& operator*()
    {
        return *_ptr;
    }
    T* operator->()
    {
        return _ptr;
    }
    void Release()
    {
        if (--(*_refcount) == 0)
        {
            if(_ptr)
            delete _ptr;
            _ptr = NULL;
        }
        delete _refcount;
    }
private:
    T *_ptr;
    int* _refCount;
    D _del;
};

猜你喜欢

转载自blog.csdn.net/qqkb1016/article/details/80221038