智能指针

1:智能指针的发展历史
<1>理解: 在动态内存分配中,为解决抛异常执行流跳转,导致内存泄漏的问题,产生了一个智能指针的类(smart pointer),智能指针是基于RAII的设计思想(资源分配即初始化,资源的有效期与持有对象的生命期严格绑定),期待它能像指针一样,智能的管理指针所指向的动态资源的释放。
<2>开始是auto_ptr(C++98):设计思想是管理权的转移,管理权在谁手上谁才有释放资源的权利,一块内存区域只能有一个管理员,通过赋值运算符赋值时,实际上也将管理权赋给了对方,auto_ptr有很大的缺陷,会造成野指针的问题,_owner会将错误延迟(比如越界问题,当在常量区时,会报错,在其他区域时不一定会报错,因为检查越界是按标志位处检查)这将是一个潜在的危险,最好不要使用auto_ptr;
<3> 后来由于 auto_ptr 会造成野指针的问题,有开源组织写了boost库(C++03之后的事),有三个指针指针:
scoped_ptr:守卫指针,防拷贝,是一种简单粗暴的方法;
shared_ptr:共享指针,引用计数,功能齐全;
weak_ptr:解决shared_ptr循环引用的缺陷问题,搭配shared_ptr使用;
<4>C++标准库参考boost库(C++11),设计了三个智能指针,unique_ptr=scoped_ptr , shared_ptr , weak_ptr

2:模拟实现
auto_ptr
设计思路:成员函数有_ptr和 _owner,构造函数时,最开始就将对象的_owner赋为true,在拷贝构造或赋值操作后,将_owner改为false,这就实现了管理权的转移,使一块空间只能有一个管理员。析构时,只有_owner为true才有资格释放空间。这个做法会造成野指针的问题。下图会做解释。最好不要使用auto_ptr。

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* ptr = NULL)
        :_ptr(ptr)
        ,_owner(true)
    {}

    AutoPtr(AutoPtr<T>& ap)
        :_ptr(ap._ptr)
        ,_owner(ap._owner)
    {
        ap._owner = false;//ap的管理权到了this的手上,实现了管理权转移
    }

    AutoPtr<T>& operator=(AutoPtr<T>& ap)
    {
        if(_ptr != ap._ptr)//自身不给自身赋值
        {
            if(_ptr)
            {
                delete _ptr;
                _ptr = ap._ptr;
                ap._owner = false;
                _owner = true;
            }
        }
        return *this;
    } 

    ~AutoPtr()
    {
        if(_owner)
        {
            cout<<"~AutoPtr()"<<endl;
            delete _ptr;
            _ptr = NULL;
            _owner = false;
        }
    }

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

    T* operator->()
    {
        return _ptr;
    }
private:
    T* _ptr;
    bool _owner; //管理员
};

//测试
void TestAutoPtr()
{
    AutoPtr<int> ap1(new int(10));
    //AutoPtr<int> ap2 = ap1;//编译器优化,调的是拷贝构造
    AutoPtr<int> ap2(ap1);
    //AutoPtr<int> ap3(new int(20));
    //ap3 = ap1;

    cout<<*ap1<<endl;
    cout<<*ap2<<endl;
    //cout<<*ap3<<endl;
}

这里写图片描述

改进版:拷贝构造和赋值操作后,将原来的指针置空,从而实现管理权的转移。

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* ptr = NULL)
        :_ptr(ptr)
    {}
    AutoPtr(AutoPtr<T>& ap)
        :_ptr(ap._ptr)
    {
        ap._ptr = NULL; //防止野指针
    }
    AutoPtr<T>& operator=(AutoPtr<T>& ap)
    {
        if(_ptr != ap._ptr)
        {
            if(_ptr)
            {
                delete _ptr;
                _ptr = ap._ptr;
                ap._ptr =NULL;
            }
        }
        return *this;
    }
    ~AutoPtr()
    {
        if(_ptr)
        {
            cout<<"~AutoPtr()"<<endl;
            delete _ptr;
            _ptr = NULL;
        }
    }
    T* operator->()
    {
        return _ptr;
    }
    T& operator*()
    {
        return *_ptr;
    }
protected:
    T* _ptr;
};

void TestAutoPtr()
{
    AutoPtr<int> ap1(new int(10));
    AutoPtr<int> ap2(ap1);
    cout<<*ap2<<endl;
    AutoPtr<int> ap3(new int(20));
    ap3 = ap2;
    cout<<*ap3<<endl;

    //cout<<*ap1<<endl;//不能再输出,会出错
}

这里写图片描述

scoped_ptr
scoped_ptr(守卫指针)主要是为了防拷贝(no-copyable),为了防止多次析构同一指针所指向的 对象。防止拷贝主要有两个条件:(1) 设置保护限定符为私有。将拷贝构造和赋值运算符重载设置为私有,以免别人写了拷贝构造和赋值运算符重载。 (2) 对拷贝构造和赋值运算符重载进行只声明不定义。否则,编译器会自动合成拷贝构造和赋值运算符重载,达不到防止拷贝的目的。

template<class T>
class ScopedPtr
{
public:
    ScopedPtr(T* ptr)
        :_ptr(ptr)
    {}
    ~ScopedPtr()
    {
        delete _ptr;
    }
    T& operator*()
    {
        return *_ptr;
    }
    T* operator->()
    {
        return _ptr;
    }
private:
    ScopedPtr(const ScopedPtr<T>&);
    ScopedPtr<T>& operator=(const ScopedPtr<T>&);
protected:
    T* _ptr;
};

void TestScopedPtr()
{
    ScopedPtr<int> sp1 = new int(10);
    ScopedPtr<int> sp2(sp1);
}

shared_pr
shared_ptr(共享指针)的思想是引用计数,功能齐全。引入指针变量_refCount,每增加一个指针指向同一空间,对_refCount++,只有当一个对指针指向该空间时,也就是_refCount=1时,才能释放空间。解决了多个指针对象指向同一块空间造成的内存泄漏问题。

template<class T>
class SharedPtr
{
public:
    SharedPtr(T* ptr = NULL)
        :_ptr(ptr)
        ,_refCount(new int(1))
    {}

    SharedPtr(const SharedPtr<T>& sp)
        :_ptr(sp._ptr)
        ,_refCount(sp._refCount)
    {
        ++(*_refCount);
    }

    SharedPtr<T>& operator=(const SharedPtr<T>& sp)
    {
        if(_ptr != sp._ptr)
        {
            if(--(*_refCount) == 0)
            {
                delete _ptr;
                delete _refCount;
            }
            _ptr = sp._ptr;
            _refCount = sp._refCount;
            ++(*_refCount);
        }
        return *this;
    }

    ~SharedPtr()
    {
        cout<<"~SharedPtr()"<<endl;
        if (_ptr)
        {
            if(--(*_refCount) == 0)
            {
                delete _ptr;
                delete _refCount;
            }
        }
    }

    int RefCount()
    {
        return *_refCount;
    }

    T* GetPtr()
    {
        return _ptr;
    }

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

protected:
    T* _ptr;
    int* _refCount;
};

void TestSharedPtr()
{
    SharedPtr<int> s1 = new int(10);
    SharedPtr<int> s2 = new int(20);
    //s2 = s1;
    //cout<<*s2<<endl;
    //cout<<*s1;

    SharedPtr<int> s3(s2);
    cout<<*s3<<endl;
}

这个SharedPtr是简化版本,只实现了最主要的部分,Boost库中的实现复杂的多,所考虑的问题更加全面。

weak_ptr
弱引用weak_ptr是为了配合shared_ptr而产生的,解决shared_ptr循环引用的问题。弱引用并不修改该对象的引用计数,意味着不对内存进行管理。弱引用能检测到所管理的对象是不是已经释放,从而避免非法访问。

//weak_ptr不增加引用计数,不能单独使用。
template<class T>
class WeakPtr
{
public:
    WeakPtr()
        :_ptr(NULL)
    {}
    WeakPtr(const SharedPtr<T>& sp)
        :_ptr(sp.GetPtr())//获取SharedPtr的_ptr
    {}
    T& operator*()
    {
        return *_ptr;
    }
    T* operator->()
    {
        return _ptr;
    }
protected:
    T* _ptr;
};

3:分析循环引用及解决方案
指针1的释放依赖于指针2的释放,指针2的释放依赖于指针1的释放。构成循环,不能释放,造成内存泄漏。
循环引用需要用到shared_ptr的use_count函数,用来获得当前的引用计数,可以使用boost库中的,也可以使用c++标准的,需要用到c++11,以下例子在vs2013下实现。

#include<iostream>
#include<memory>
#include<windows.h>
using namespace std;

//循环引用
struct ListNode
{

    ~ListNode()
    {
        cout << "~ListNode()" << endl;
    }
    shared_ptr<ListNode> _prev;
    shared_ptr<ListNode> _next;
};

void TestCycle()
{
    shared_ptr<ListNode > prev(new ListNode);
    shared_ptr<ListNode > next(new ListNode);
    cout << "prev->count " << prev.use_count() << endl;
    cout << "next->count " << next.use_count() << endl;
    prev->_next = next;
    next->_prev = prev;
    cout << "prev->count " << prev.use_count() << endl;
    cout << "next->count " << next.use_count() << endl;
}

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

这里写图片描述

解决:通过weak_ptr

struct ListNode
{

    ~ListNode()
    {
        cout << "~ListNode()" << endl;
    }
    weak_ptr<ListNode> _prev;
    weak_ptr<ListNode> _next;
};

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值