智能指针

在c++中,动态内存的的开辟与释放有人为的去管理的话,有时会出现问题(忘记释放,执行流的跳转…)从而导致内存的泄漏问题。为了解决这一问题引入了智能指针,即开辟与释放是自动完成的。
RAII
智能指针实现的基础,即通过一个类来管理,由对象的构造函数来获取资源(构造即初始化),析构函数去释放资源。
智能指针就是基于这样的机制,当然指针的基本功能智能指针也会拥有。
auto_ptr,带有严重缺陷的智能指针。

#include <iostream>
using namespace std;

template <class T>

class Auto_Ptr
{
public:
    Auto_Ptr(T* p)
        :_p(p)
    {
        cout << "Auto_Ptr(T* p)" << endl;
    }
    ~Auto_Ptr()
    {
        cout << "~Auto_Ptr()" << endl;
        delete _p;
    }

    Auto_Ptr(const Auto_Ptr<T>&s)
        :_p(s._p)
    {
        s._p = NULL;
    }
    Auto_Ptr<T> operator=(Auto_Ptr<T>& s)
    {
        if (_p != s._p)
        {
            if (_p)
            {
                delete _p;
            }
            _p = s._p;
            s._p = NULL;
        }
        return *this;
    }
    T& operator*()
    {
        return *_p;
    }
    T* operator->()
    {
        return _p;
    }
private:
    T* _p;
};

void Test()
{
    int* p = new int;
    Auto_Ptr<int> ap(p);
    *ap = 10;
}

int main()
{
    Test();

    system("pause");
    return 0;
}

这是auto_ptr的简单实现,它能做到像正常指针一样正常使用,也能自动释放,但前文说它存在重大的缺陷,观察它的拷贝构造,与赋值运算符的重载,可以发现,愿对象的指针成为了空,这就是auto_ptr的机制(管理员权限管理),这显然是不符合指针习惯的,实现机制导致了永远只有一个指针指向,其余指针全为NULL,如果在后续程序中不小心使用了成为NULL的指针,程序会崩溃。

作为最早的智能指针存在很大缺陷,c++发展了很长时间,才将这个问题解决,boost系列的智能指针解决了这个问题。下面主要介绍boost系列的智能指针,c++11也有同样的系列,不过基本机制是相同的。

scoped_ptr:守卫指针,不让拷贝赋值

template<class T>
class Scoped_Ptr
{
public:
    Scoped_Ptr(T* p)
        :_p(p)
    {}
    ~Scoped_Ptr()
    {
        delete _p;
    }
    T& operator*()
    {
        return *_p;
    }
    T* operator->()
    {
        return _p;
    }
private:
    Scoped_Ptr(const Scoped_Ptr<T>&s);
    Scoped_Ptr<T> operator=(Scoped_Ptr<T>& s);
private:
    T* _p;
};

既然构造会与赋值会出现问题,那么便不让构造与赋值,封装成私有,并且只定义不声明,当然声明是可以的,但这样做没有意义。

shared_ptr分享数组:
简单的不让拷贝,赋值显然是不行的,引用计数可以解决这样的问题(写时拷贝中也使用过这样的方式)。

template <class T>
class Shared_Ptr
{
public:
    Shared_Ptr(T* p)
        :_p(p)
        , count(new int(1))
    {}

    ~Shared_Ptr()
    {
        IfDelete();
    }

    void IfDelete()
    {
        if (--(*count) == 0)
        {
            delete _p;
            _p = NULL;
            delete count;
            count = NULL;
        }
    }

    Shared_Ptr(Shared_Ptr& p)
        :_p(p._p)
        , count(p.count)
    {
        ++(*count);
    }
    Shared_Ptr& operator=(Shared_Ptr& p)
    {
        if (_p != p._p)
        {
            IfDelete();
            _p = p._p;
            count = p._count;
            ++(*count);
        }
        return *this;
    }

    T& operator*()
    {
        return *_p;
    }
    T* operator->()
    {
        return _p;
    }
private:
    T* _p;
    int* count;
};

在这里实现的shared_ptr是非常简单的,但引用计数的使用是解决这个问题的主要手段,有兴趣的可以去看下boost的源码。
shared_array:共享数组
boost里面对数组做了单独的处理,new与delete,new[]与delete[]这些是需要配套使用的,否则在管理自定义类型时就有可能出现程序崩溃的问题。

weak_ptr:弱指针
shared_ptr 存在这一个缺陷,所以需要和weak_ptr共同使用。
问题:循环引用
这里写图片描述
我们实例化两个shared_ ptr的对象,A空间,B空间。那么按照shared_ ptr的机制,此时ptr1和ptr2指针所指向的空间计数count都为1。

假设两个shared_ ptr的对象中还有next指针和prev指针。我们让A中的next指针指向ptr2指向的空间时,B所指向的空间中的count计数变成2;B的prev指针指向A指向的空间,B所指向的空间计数count也变成了2。

如果要释放A空间,即A空间的引用计数要为0,此时存在B空间的prev指向A空间,故此要释放B空间,prev才会被释放掉,(prev依赖B空间存在),此时需要释放B空间,但是B空间的释放有需要将A空间中的next释放掉,但next的释放依赖A空间的释放,会发现永远也不会释放。

为解决循环引用的问题,需要配合weak_ptr一起使用。
它并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱指针能检测到所管理的对象是否已经被释放,从而避免访问非法内存。

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

    WeakPtr(const SharedPtr<T>& s)
        :_p(s._p)
    {}

    ~WeakPtr()
    {
        if (_p)
        {
            delete _p;
            _p = NULL;
        }
    }

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

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

private:
    T* _p;
};

显然可以看出weak_ptr并不参与管理,一开始就为NULL,它一定是通过已有对象去构造的,但它不存在引用计数,意味着它不会参与原对象的管理,(不负责释放)。

通看上述代码,在管理内存时存在缺陷,delete,delete[]并不能管理所有的释放,在auto_ptr中干脆就没有delete[],这意味着其不会管理数组。为了管理其他类型引入了定制删除器。

#include <boost/shared_ptr.hpp>
using namespace boost;
// 定置的删除器仿函数
class FClose
{
public :
    void operator () (void* ptr)
    {
        fclose((FILE*)ptr);
    }
};
class Free
{
public :
    void operator () (void* ptr)
    {
        free(ptr);
    }
};
void Test ()
{
    // 定制删除器
    shared_ptr<FILE > p1( fopen("test.txt" , "w"), FClose());
    // 定制删除器和分配器
    shared_ptr<int > p2((int *)malloc( sizeof(int )), Free(), allocator<int >());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值