1. 程式人生 > >模擬實現智慧指標

模擬實現智慧指標

眾所周知,智慧指標是用來管理指標的,為了避免開闢了空間而忘記釋放的情況。
下面我們分別來模擬實現以下boost庫中三種智慧指標的實現,他們分別是auto ptr,scoped ptr,shared ptr。

一.auto ptr

  1. 舊版本的實現:
template <typename T>
class Auto_ptr
{
public:
    Auto_ptr(T*ptr = NULL) :_ptr(ptr), state(true)
    {}
    ~Auto_ptr()
    {
        if (state&&NULL != _ptr)
        {
            delete
_ptr; } } Auto_ptr(const Auto_ptr<T>&sp) { _ptr = sp._ptr; sp.state = false;//賦值以後將狀態置為false; } Auto_ptr<T>&operator=(const Auto_ptr<T>&sp) { if (this != &sp) { if (_ptr) { delete
_ptr; _ptr = NULL; } _ptr = sp._ptr; sp.state = false;//賦值以後將狀態置為false; } return *this; } private: T*_ptr; bool state; };

2.新版本的實現:
賦值以後將原指標置為空,而不是改變狀態

3.總結
auto ptr只適用於單個的指標的情況,當進行物件之間的賦值的時候會使所有權轉移。所以建議在任何情況下都不使用auto ptr。

二.scoped ptr

scoped ptr是對auto ptr的改進,將拷貝構造以及賦值操作符的過載設為私有。適用於開闢一個型別空間的指標以及多個型別空間的指標。

三.shared ptr

template<typename T>
class Shared_ptr
{
public:
    Shared_ptr(T*ptr = NULL) :_ptr(ptr), pcount(NULL)
    {
        if (NULL != _ptr)
        {
            pcount = new int(1);
        }
    }
    ~Shared_ptr()
    {
        if (_ptr&&--(*pcount) == 0)
        {
            delete _ptr;
            delete pcount;
        }
    }
    Shared_ptr(const Shared_ptr<T>&sp)
    {
        _ptr = sp._ptr;
        pcount = sp.pcount;
        ++(*pcount);
    }
    Shared_ptr<T>&operator=(const Shared_ptr<T>&sp)
    {
        if (this != &sp)
        {
            if (_ptr&&--(*pcount)==0)
            {
                delete pcount;
                delete _ptr;
            }
            pcount = sp.pcount;
            _ptr = sp._ptr;
            ++(*pcount);
        }
        return *this;
    }
    int use_count()
    {
        return *pcount;
    }
private:
    T*_ptr;
    int*pcount;
};

使用引用計數進行實現,適用於開闢多個空間的指標和一個空間的指標。可以進行指標間的賦值。但是它同時也帶來了一些問題。
1. 引用計數更新存在著執行緒安全(這裡暫且不提)
2. 迴圈引用
3. 定置刪除器

這裡重點講一下迴圈引用的問題:

struct ListNode
{
    shared_ptr<ListNode> _prev;
    shared_ptr<ListNode> _next;
    /*weak_ptr<ListNode > _prev;
    weak_ptr<ListNode > _next;*/
    ~ListNode()
    {
        cout << "~ListNode()" << endl;
    }
};
void Test()
{
    // 迴圈引用問題
    shared_ptr <ListNode > p1(new ListNode());
    shared_ptr <ListNode > p2(new ListNode());
    cout << "p1->Count:" << p1.use_count() << endl;//1
    cout << "p2->Count:" << p2.use_count() << endl;//1
    // p1節點的_next指向 p2節點
    p1->_next = p2;
    // p2節點的_prev指向 p1節點
    p2->_prev = p1;
    cout << "p1->Count:" << p1.use_count() << endl;//2
    cout << "p2->Count:" << p2.use_count() << endl;//2
}

由程式碼可知p1本就維護一塊空間,現將p1賦值給p2的_next也就兩個指標在維護一塊空間,p1的引用計數為2,p2的引用計數也為2。
(1)當p2出了作用域,引用計數變為1,不為0,所以沒有釋放空間。
(2)當p1出了作用域,引用計數變為1,不為0,所以沒有釋放空間。
畫個圖來解釋一下(1)(2):
這裡寫圖片描述
p2出作用域,它本身要呼叫解構函式,所以引用計數減1,但此時p1的_next的值是p2,也就是說p2同時被p1管理著,p1的空間沒有釋放,所以p2的引用計數不會變為0。
同理,因為p2沒有釋放,p1也無法釋放。

解決:將ListNode中_prev和_next的型別設為weak ptr型別。
原因:weak ptr和shared ptr都是引用計數基類的派生類並且weak ptr維護的引用計數和shared ptr的不一樣。