C++智能指针深层剖析,模拟实现

C++中使用new和delete动态开辟和释放内存。但有时我们会忘记释放内存,这样就有可能出现内存泄漏等一些危险的情况。有时在尚有指针引用内存的情况下我们就释放了它,在这种情况下就会产生引用非法内存的指针。
为了更容易,也更安全地使用动态内存,C++提供了智能指针类型来管理动态对象。智能指针遵从RAII规则,我们先来介绍什么是RAII.

RAII:是一种规范,一种解决问题的思想。负责资源分配即初始化,我们定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。

1,auto_ptr
auto_ptr是早期的智能指针设计,它是有缺陷的,其中使用了管理权转移。我们来简单模拟auto_ptr,更深层的理解它。

template<class T>
class AutoPtr
{
public:
    //RAII
    AutoPtr(T* ptr = NULL)
        :_ptr(ptr)
    {}

    //ap2(ap1)
    AutoPtr(AutoPtr& ap)//拷贝构造
        :_ptr(ap._ptr)
    {
        ap._ptr = NULL;
    }

    //ap2 = ap3
        AutoPtr& operator = (AutoPtr& ap)//赋值运算符重载
    {
            if (this != &ap)
            {
                delete _ptr;
                _ptr = ap._ptr;
                ap._ptr = NULL;//管理权转移
            }
            return *this;
    }

        ~AutoPtr()//析构
        {
            if (_ptr)
            {
                cout << _ptr << endl;
                delete _ptr;
                _ptr = NULL;
            }
        }

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

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

int main()
{
    AutoPtr<int> ap1(new int(10));
    AutoPtr<int> ap2 = ap1;//ap2(ap1)error//ap1地址指向随机
    AutoPtr<int> ap3(new int(20));
    ap2 = ap3;//ap3地址为随机值
    system("pause\n");
    return 0;
}

我们来看一下调试过程中ap1,ap2,ap3的地址有何变化
C++智能指针深层剖析,模拟实现_第1张图片

C++智能指针深层剖析,模拟实现_第2张图片
从调试过程明显看出auto_ptr的缺陷,当想通过原先的指针访问原先所管理的空间时,会找不到原来的空间,因此,我们要避免使用auto_ptr。
注意以下几点:
(1)aotuptr不能共享所有权
(2)autoptr不能指向数组delete
(3)autoptr不能作为容器的成员
(4)不能通过赋值操作来初始化autoptr
(5)不要把autoptr放入容器

2,scoped_ptr
这是一种高效、简洁的智能指针,实现原理是,将拷贝构造函数和赋值运算符的重载写成私有,且只声明不定义。这是一种简单粗暴的防拷贝方式,在不需要拷贝和赋值时,我们尽量使用scoped_ptr.

template<class T>
class ScopedPtr
{
public:
    //RAII
    ScopedPtr(T* ptr = NULL)
        :_ptr(ptr){}

    ~ScopedPtr()
    {
        if (_ptr)
        {
            delete _ptr;
        }
    }

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

    T& operator->()
    {
        return _ptr;
    }
protected:
    T* _ptr;
private://只声明不定义,且声明为私有
    ScopedPtr(const ScopedPtr& sp);
    ScopedPtr& operator = (const ScopedPtr&);
};

int main()
{
    ScopedPtr<int> sp1(new int(30));
    //ScopedPtr sp2(sp1);//错误
    return 0;
}

3,shared_ptr
shared_ptr允许多个指针指向同一个对象,我们可以认为每一个shared_ptr都有一个关联的计数器,通常称其为引用计数,无论何时我们拷贝一个shared_ptr,计数器就会增加当我们给shared_ptr赋予一个新值或是shared_ptr被销毁时,计数器就会递减。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。但shared_ptr存在循环引用的缺陷,可能导致空间无法正常清理,我们用仿函数模拟定制删除器来解决此类问题。

template<class T>
struct Delete  //清理单个空间
{
    void operator()(T* ptr)//定制删除器(仿函数)
    {
        delete ptr;
    }
};

template<class T>
class DeletArray//清理数组
{
public:
    void operator()(T* ptr)
    {
        delete[] ptr;
    }
};

template<class T,class D = Delete>//默认是delete,不是delete[]
class SharedPtr
{
public:
    SharedPtr(T *ptr)
        :_ptr(ptr)
        , _countRef(new int(1)){}



    SharedPtr& operator= (const SharedPtr& sp)//赋值运算符的重载
    {
        if (_ptr != sp._ptr)
        {
            Release();
        _ptr = sp._ptr;
        _countRef = sp._countRef;
        ++(*_countRef);
         }
        return *this;
    }

    SharedPtr(const SharedPtr& sp)//拷贝构造
        :_ptr(sp._ptr)
        , _countRef(sp._countRef)
    {
        ++(*_countRef);
    }

    inline void Release()
    {
        if (--(*_countRef) == 0)
        {
            cout << _ptr << endl;
            _del( _ptr);
            delete _countRef;
        }
    }

    ~SharedPtr()
    {
        Release();
    }

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

    int UseCount()//引用计数
    {
        return *_countRef;
    }

protected:
    T* _ptr;
    int* _countRef;//引用计数

    D _del;//定制的仿函数删除器
};

boost库中提供一个weak_ptr来解决循环引用的问题,weak_ptr是一种不控制所指向对象生存期 的智能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。

由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock,此函数检查weak_ptr指向的对象是否仍存在,如果存在,lock返回一个指向共享对象的shared_ptr.

shared_ptr<int> p(new int (10));
weak_ptr<int> wp(p);//wp弱共享p,p的引用计数不改变
if(shared_ptr<int> np = wp.lock())//若np不为空则条件成立
{
//np与p共享对象
}

我们简单模拟一下weak_ptr的实现

template<class T>
class WeakPtr;

template<class T>
class SharedPtr
{
    friend class WeakPtr;
public:
    SharedPtr(T* ptr)
        :_ptr(ptr)
        , _refcount(new int(1))
    {}

    SharedPtr(const SharedPtr& sp)
        :_ptr(sp._ptr)
        , _refcount(sp._refcount)
    {
        ++(*_refcount);
    }

    void Relese()
    {
        if (--(*_refcount) == 0)
        {
            if (_ptr)
            {
                cout << this << endl;
                delete _ptr;
            }
            delete _refcount;
        }
    }

    SharedPtr& operator = (const SharedPtr& sp)
    {
        if (_ptr != sp._ptr)
        {
            Relese();
            _ptr = sp._ptr;
            _refcount = sp._refcount;
            ++(*_refcount);
        }
        return *this;
    }

    ~SharedPtr()
    {
        Relese();
    }

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

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

template<class T>
class WeakPtr
{
public:
    WeakPtr()
        :_ptr(NULL)
    {}

    WeakPtr(const SharedPtr sp)
        :_ptr(sp._ptr)
    {}

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

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

struct ListNode
{
    WeakPtr _prev;
    WeakPtr _next;
};

void Test()
{
    SharedPtr cur = new ListNode;
    SharedPtr next = new ListNode;
    cur->_next = next;
    next->_prev = cur;
}

int main()
{
    Test();
    system("pause");
    return 0;
}

你可能感兴趣的:(cpp,C++)