shared_ptr:定製刪除器 和 循環引用

前面我們介紹智能指針的時候,說了兩個智能指針分別是:auto_ptr和scoped_ptr,auto_ptr有問題,爲此,scoped_ptr的出現解決了這個問題,scoped_ptr太霸道,不允許別人和他共用一塊內存空間,所以,我們還得想辦法解決這個問題。

回想我們以前看過內容,當提到共用一塊內存空間的時候,我們會想到什麼?

當然是深拷貝和淺拷貝了,最後我們是不是有給出了一個寫時拷貝,就是通過引用計數的方法來實現共用一塊內存空間的。對於這個問題,C++中也給出了一個相同方法的解決方案,這就是shared_ptr。

首先,我們還是模擬一下shared_ptr:

template <typename T>
class SharedPtr
{
public:
    SharedPtr(T*ptr)
        :_Ptr(ptr)
        , _pcount(NULL)
    {
        if (_ptr)
        {
            _pcount=new int(1);
        }
    }

    SharedPtr(const SharedPtr<int> & sp)
    {
        _ptr = sp._ptr;
        _pcount = sp._pcount;
        ++(*_pcount);
    }

    ~SharedPtr()
    {
        if (0 == (--*_pcount))
        {
            delete _ptr;
            delete _pcount;
        }
    }

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

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

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

private:
    T* _ptr;
    int * _pcount;
};

定製刪除器

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//爲解決文件指針
struct Fclose
{
    void operator()(FILE*& fp)
    {
        cout<< "Fclose()" << endl;
        fclose(fp);
        fp = NULL;
    }
};
//爲解決malloc開闢的空間
template<class T>
struct Free
{
    void operator()(T*& p)
    {
        free(p);
        p = NULL;
    }
};
//一般情況下(使用new動態開闢的空間)
template<class T>
class Delete
{
public :
    void operator()(T*& p)
    {
        delete p;
        p = NULL;
    }
};
template<class T, class Destory = Delete<T> >
class SharedPtr
{
public :
    SharedPtr(T* ptr = 0)//構造函數
        :_ptr(ptr)
        ,_pCount(NULL)
    {
        if (_ptr)
        {
            _pCount = new int(1);
        }
    }
    SharedPtr(const SharedPtr<T>& sp)//拷貝構造函數
        :_ptr(sp._ptr)
        , _pCount(sp._pCount)
    {
        if (_ptr)
        {
            ++GetRef();
        }
    }
    //sp1 = sp2
    SharedPtr<T>& operator=(const SharedPtr<T>& sp)
    {
        //可有三種情況:①sp1._ptr = NULL  ②sp1的引用計數爲1  ③sp1的引用計數大於1
        if (this != &sp)
        {
            Release();
            _ptr = sp._ptr;
            _pCount = sp._pCount;
            ++GetRef();
        }
        return *this;
    }
    //輔助函數
    void Release()
    {
        if (_ptr && --GetRef() == 0)
        {
            Destory()(_ptr);
            delete _pCount;
            _pCount = NULL;
        }
    }
 //析構函數
    ~SharedPtr()
    {
        Release();
    }

    int& GetRef()
    {
        return *_pCount;
    }
private:
    T* _ptr;
    int* _pCount;
};
void Test2()
{
    FILE* sp1 = fopen("test.txt", "rb");
    SharedPtr<FILE, Fclose> sp2(sp1);

    int* sp3 = (int*)malloc(sizeof(int));
    SharedPtr<int, Free<int> >sp4(sp3);

    int* sp5 = new int(1);
    SharedPtr<int> sp6(sp5);
}

int main()
{
    Test2();
    return 0;
}

循環引用

通過引用計數,我們解決了智能指針共享一塊空間的問題,不過,shared_ptr還是有點問題,那就是循環引用的問題,什麼是循環引用的問題呢?先來看一個例子:

struct Node  
{  
    int _data;  
    shared_ptr<Node> _next;  
    shared_ptr<Node> _prev;  
};  

void FunTest()  
{  
    shared_ptr<Node> sp1(new Node);  
    shared_ptr<Node> sp2(new Node);  
    cout << sp1.use_count() << endl;  
    cout << sp2.use_count() << endl;  

    sp1->_next = sp2;  
    sp2->_prev = sp1;  
    cout << sp1.use_count() << endl;  
    cout << sp2.use_count() << endl;  
}

我們先定義一個雙鏈表節點,然後,我們再創建兩個節點,並且讓其鏈接起來,之後,我們再想要釋放這兩個節點的時候,會發現一個很有意思的現象,那就是,釋放sp1的時候,必須先釋放sp2,而想要釋放sp2,那麼必須先釋放sp1,這就是循環引用的例子。

如圖:

這裏寫圖片描述
這是我畫的兩個節點,sp1的nest指向sp2,同時,sp2的prev指向sp1。那麼sp1所指向的內存就被兩個指針所管理,分別是:sp1和sp2->_prev;同樣的,sp2所指向的內存空間就會被sp2和sp1->_nest所管理。

如果現在想要釋放的是sp1,那麼釋放內存的時候其實是將其引用計數減去1,不是將這段空間釋放。

那麼,如何解決這個問題呢?

這裏就不得不說:weak_ptr 了。

發佈了121 篇原創文章 · 獲贊 90 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章