智能指针(下)-----boost库智能指针,定制删除器、循环引用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Pg_dog/article/details/70185446

上一篇我们已经详细讲解了智能指针的基础性知识和auto_ptr的模拟实现。
今天呢我们来讲解boost库的发展。
在C++11标准出来之前,C++98标准中都一直只有一个智能指针auto_ptr,我们知道,这是一个失败的设计。它的本质是管理权的转移,这有许多问题。而这时就有一群人开始扩展C++标准库的关于智能指针的部分,他们组成了boost社区,他们负责boost库的开发和维护。其目的是为C++程序员提供免费、同行审查的、可移植的程序库。boost库可以和C++标准库完美的共同工作,并且为其提供扩展功能。现在的C++11标准库的智能指针很大程度上“借鉴”了boost库。

1,scoped_ptr的模拟实现
scoped_ptr是一种简单粗暴的设计,它本质就是防拷贝,避免出现管理权的转移。这是它的最大特点,所以他的拷贝构造和赋值运算符重载只是只声明不定义,但是为了防止有的人在类外定义,所以将函数声明为protected。但这则也是他最大的问题所在,就是不能赋值拷贝,也就是说功能不全。

template<class T>
class ScopedPtr
{
public:
    ScopedPtr(T* ptr = NULL)
        :_ptr(ptr)
    {
        cout << "ScopedPtr" << endl;
    }

    ~ScopedPtr()
    {
        delete _ptr;
        cout << "~ScopedPtr" << endl;
    }

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

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

    bool operator==(const ScopedPtr<T>& s)
    {
        return _ptr == s._ptr;
    }

    bool operator!=(const ScopedPtr<T>& s)
    {
        return _ptr != s._ptr;
    }

    void Reset(T* ptr = NULL)
    {
        if (_ptr != ptr)
        {
            delete _ptr;
        }
        _ptr = ptr;
    }
protected:

    ScopedPtr(ScopedPtr<T>& s); //防拷贝(只声明不定义,为防止别人在类外定义,就将他声明为protected)
    ScopedPtr<T>& operator=(ScopedPtr<T>& s);

protected:
    T* _ptr;
};

2,shared_ptr的模拟实现
这是比较完善的一个智能指针,他是通过指针保持某个对象的共享拥有权的智能指针。若干个shared_ptr对象可以拥有同一个对象,该对象通过维护一个引用计数,记录有多少个shared_ptr指针指向该对象,最后一个指向该对象的shared_ptr被销毁或重置时,即引用计数变为0时,该对象被销毁。销毁对象时使用的是delete表达式或是在构造shared_ptr时传入的自定义删除器(delete),这后面会有详细讲解,但是shared_ptr指针同样拥有缺陷,那就是循环引用,和线程安全问题,这也在后面讲解。先来模拟实现一下shared_ptr指针。

template<class T>
class SharedPtr
{
public:
    SharedPtr(T* ptr = NULL)
        :_ptr(ptr),_count(new int(0))
    {
        if (_ptr != NULL)
        {
            (*_count)++;
        }
    }

    SharedPtr(const SharedPtr<T>& s)
    {
        _ptr = s._ptr;
        _count = s._count;
        if (_ptr != NULL)
        {
            (*_count)++;
        }
    }

    SharedPtr<T>& operator=(const SharedPtr<T>& s)
    {
        if (this != &s)   //排除自己给自己赋值
        {
            if (--(*_count) <= 0)  //正常情况赋值
            {
                delete _ptr;
                delete _count;
            }
            else  //指向同一个对象的指针互相赋值
            {}
            _ptr = s._ptr;
            _count = s._count;
            (*_count)++;
        }
        return *this;
    }

    ~SharedPtr()
    {
        if (--(*_count) == 0)
        {
            delete _ptr;
            delete _count; //别忘了delete维护的引用计数
        }
    }

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

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

    bool operator ==(const SharedPtr<T>& S)
    {
        return (_ptr == s._ptr);
    }

    bool operator !=(const SharedPtr<T>& S)
    {
        return (_ptr != s._ptr);
    }

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

线程安全问题
因为使用引用计数值位判定指标,所以在多线程的环境下是不安全的。会因线程调用的先后顺序不同导致错误产生。对于这种问题,解决方法一般是加锁,对引用计数进行加,保证操作是互斥的。(这里暂且说这些,后续文章会有提及。)

循环引用问题
针对循环引用,我来举个例子使大家能更好的理解。看下面代码:

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

    ListNode(int x)
        :_data(x),_next(NULL),_prev(NULL)
    {}
    ~ListNode()
    {
        cout << "~ListNode()" << endl;
    }
};

void test()
{
    shared_ptr<ListNode> A(new ListNode(1));
    shared_ptr<ListNode> B(new ListNode(2));

    //if (A && B)  //将这五行代码放开来会出现什么情况
    //{
    //  A->_next = B;
    //  B->_prev = A;
    //}

    cout << "A._count:" << A.use_count() << endl;
    cout << "B._count:" << B.use_count() << endl;
}

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

运行结果
这里写图片描述

但是如果我将上面代码中屏蔽的那五行放开来,会出现什么结果呢?看下图:
这里写图片描述
有什么不同?对比上图,可以发现下面图的两个节点维护的引用计数值为2,他们也没有调用析构函数造成内存泄露。这是什么原因造成的?我们用一张图来解释。
这里写图片描述
而要解决循环引用的问题,就牵扯到了我们后面将要讲的一个指针weak_ptr。具体看后面。

定制删除器(仿函数)
经上面分析,我们可以看到,上面的指针不能用于文件的关闭,也不能用于管理mallocnew[]开辟的动态内存的释放,所以我们可以运用仿函数来定制删除器。如下:

template<class T>
struct DeleteArray  //用于new[]开辟的动态内存释放
{
    void operator()(T* ptr)
    {
        cout << "A" << endl;
        delete[] ptr;
    }
};

struct Fclose  //用于文件关闭
{
    void operator()(FILE* ptr)
    {
        cout << "B" << endl;

        fclose(ptr);
    }
};

template<class T>
struct Free     //用于malloc开辟的动态内存的释放
{
    void operator()(T* ptr)
    {
        cout << "C" << endl;
        free(ptr);
    }
};

int main()
{
    shared_ptr<string> ap1(new string[10], DeleteArray<string>());
    shared_ptr<FILE> ap2(fopen("test.txt", "w"),Fclose());
    shared_ptr<int> ap3((int*)malloc(sizeof(int)), Free<int>());
    return 0;
}

3,weak_ptr
weak_ptr是一个辅助性的智能指针,结合shared_ptr指针使用,它的本质就是弱引用,并不增加引用计数值。他没有实现->和*运算符的重载,所以不能直接用它访问对象。针对循环引用这个问题,就是因为不会引起引用计数值的改变,所以我们可以将_next和_prev定义为weak_ptr指针,这样就很好地解决了循环引用的问题。

struct ListNode
{
    int _data;
    weak_ptr<ListNode> _next;  //定义为weak_ptr指针
    weak_ptr<ListNode> _prev;

    ListNode(int x)
        :_data(x),_next(NULL),_prev(NULL)
    {}
    ~ListNode()
    {
        cout << "~ListNode()" << endl;
    }
};

boost库剩余的两个指针:auto_arrshared_arr.这两个都是管理数组的,因为之前几个指针的析构函数中都是delete,不能对数组进行释放,所以我们自己定制删除器,而这两个指针就是专门管理指针的。下面来模拟实现一下。

模拟实现auto_arr

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

    ~AutoArr()
    {
        delete[] _ptr;
    }

    AutoArr(const AutoArr<T>& s)
    {
        _ptr = s._ptr;
        s._ptr = NULL;
    }

    AutoArr<T>& operator=(const AutoArr<T>& s)
    {
        if (this != &s)
        {
            _ptr = s._ptr;
            s._ptr = NULL;
        }
        return *this;
    }

    T& operator[](size_t pos)
    {
        if (_ptr == NULL)
        {
            throw a;
        }
            return *(_ptr+pos);
    }

    void set(T* ptr)
    {
        int i = 0;
        while (*(ptr + i))
        {
            *(_ptr + i) = *(ptr + i);
            i++;
        }
    }

protected:
    T* ptr;
};

模拟实现shared_arr

template<class T>

class SharedArr
{
public:
    SharedArr(T* ptr = NULL)
        :_ptr(ptr),_count(new int(0))
    {
        (*_count)++;
    }

    ~SharedArr()
    {
        delete[] _ptr;
    }

    SharedArr(const SharedArr<T>& s)
    {
        _ptr = s._ptr;
        (*_count)++;
    }

    SharedArr<T>& operator=(const SharedArr<T>& s)
    {
        if (this != &s)
        {
            if (--(*_count) <= 0)
            {
                delete _ptr;
                delete _count;
            }
            else
            { }
            _ptr = s._ptr;
            _count = s._count;
            (*_count)++;
        }
    }

    T& operator[](size_t pos)
    {
        if (_ptr == NULL)
        {
            throw 1;
        }
        return *(_ptr + pos);
    }

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

猜你喜欢

转载自blog.csdn.net/Pg_dog/article/details/70185446