智能指針

一直都知道智能指針是依靠引用計數的原理實現的,但是,自己一直都沒有嘗試自己實現一個簡單的指針指針,最近嘗試自己寫了一個智能指針的類,進行了簡單的測試,不知道會不會有bug,代碼如下:

SmartPointer.h

#include <iostream>

template <typename T>

class SmartPointer
{
    public:
    //構造函數
    SmartPointer( T *p = 0 ):_ptr( p ), _reference_count ( new size_t ){
        if( p )
            *_reference_count = 1;
        else
            *_reference_count = 0;
    }

    //拷貝構造函數(淺拷貝)
    SmartPointer( const SmartPointer& other )
    {
        if( this != &other )
        {
            _ptr = other._ptr;
            _reference_count = other._reference_count;
            (* _reference_count )++;
        }
    }

    //重載賦值操作符
    SmartPointer& operator= ( const SmartPointer& other )
    {
        if( _ptr == other._ptr )
            return *this;
        //先釋放原有所指向的對象
        releaseCount();

        _ptr = other._ptr;
        _reference_count = other._reference_count;
        //增加引用計數
        ( * _reference_count )++;
        return *this;
    }

    //重載運算符
    T& operator*()
    {
        if( _ptr )
            return *_ptr;
    }
    //重載運算符
    T* operator->()
    {
        if( _ptr )
            return _ptr;
    }
    //析構函數
    ~SmartPointer()
    {
        if( -- ( *_reference_count )  == 0 )
        {
            delete _ptr;
            delete _reference_count;
        }
    }
    private:
        T *_ptr;
        size_t *_reference_count;//指針
        void releaseCount(){
            if( _ptr  )
            {
                (* _reference_count )--;
                if( (*_reference_count) == 0 )
                {
                    delete _ptr;
                    delete _reference_count;
                }
            }
        }
};

再介紹一下常用的智能指針:
1. std::auto_ptr,有很多問題。 不支持複製(拷貝構造函數)和賦值(operator =),但複製或賦值的時候不會提示出錯。因爲不能被複制,所以不能被放入容器中。
2. C++11或boost的shared_ptr,基於引用計數的智能指針。可隨意賦值,直到內存的引用計數爲0的時候這個內存會被釋放。
3. C++11或boost的weak_ptr,弱引用。 引用計數有一個問題就是互相引用形成環,這樣兩個指針指向的內存都無法釋放。需要手動打破循環引用或使用weak_ptr。顧名思義,weak_ptr是一個弱引用,只引用,不計數。如果一塊內存被shared_ptr和weak_ptr同時引用,當所有shared_ptr析構了之後,不管還有沒有weak_ptr引用該內存,內存也會被釋放。所以weak_ptr不保證它指向的內存一定是有效的,在使用之前需要檢查weak_ptr是否爲空指針。
4. C++11引入的unique_ptr, 也不支持複製和賦值,但比auto_ptr好,直接賦值會編譯出錯。實在想賦值的話,需要使用std::move。
例如:

std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; // 編譯會出錯
std::unique_ptr<int> p3 = std::move(p1); // 轉移所有權, 現在那塊內存歸p3所有, p1成爲無效的指針.

談到智能指針互相引用成環的問題,下面舉個例子:

class B;
class A
{
public:
  shared_ptr<B>
 m_b;
};

class B
{
public:
  shared_ptr<A>
 m_a;
};

int main()
{
  while (true)
  {
    shared_ptr<A>
 a(new A);
//new出來的A的引用計數此時爲1
    shared_ptr<B>
 b(new B);
//new出來的B的引用計數此時爲1
    a->m_b
 = b; //B的引用計數增加爲2
    b->m_a
 = a; //A的引用計數增加爲2
  }

  //b先出作用域,B的引用計數減少爲1,不爲0,所以堆上的B空間沒有被釋放,且B持有的A也沒有機會被析構,A的引用計數也完全沒減少

  //a後出作用域,同理A的引用計數減少爲1,不爲0,所以堆上A的空間也沒有被釋放

}

所以在使用基於引用計數的智能指針時,要特別小心循環引用帶來的內存泄漏,循環引用不只是兩方的情況,只要引用鏈成環都會出現問題。當然循環引用本身就說明設計上可能存在一些問題,如果特殊原因不得不使用循環引用,那可以讓引用鏈上的一方持用普通指針(或弱智能指針weak_ptr)即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章