scoped_array原始碼剖析

FreeeLinux發表於2017-01-20

scoped_array很想scoped_ptr,它包裝了new[]操作符(不是單純的new)在堆上分配的動態陣列,為動態陣列提供了一個代理,保證可以正確的釋放記憶體。

原始碼剖析

scoped_array的原始碼如下:

namespace boost
{

//  scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
//  is guaranteed, either on destruction of the scoped_array or via an explicit
//  reset(). Use shared_array or std::vector if your needs are more complex.

template<class T> class scoped_array // noncopyable
{
private:

    T * px;

    scoped_array(scoped_array const &);
    scoped_array & operator=(scoped_array const &);

    typedef scoped_array<T> this_type;

    void operator==( scoped_array const& ) const;
    void operator!=( scoped_array const& ) const;

public:

    typedef T element_type;

    explicit scoped_array( T * p = 0 ) BOOST_NOEXCEPT : px( p )
    {
    }

    ~scoped_array() // never throws
    {
        boost::checked_array_delete( px );
    }

    void reset(T * p = 0) // never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT)
    {
        BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors
        this_type(p).swap(*this);
    }

    //過載了operator[]模擬陣列,但不支援*(p+i)這種訪問方式
    T & operator[](std::ptrdiff_t i) const // never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT)
    {
        BOOST_ASSERT( px != 0 );
        BOOST_ASSERT( i >= 0 );
        return px[i];
    }

    T * get() const BOOST_NOEXCEPT
    {
        return px;
    }

// implicit conversion to "bool"
#include <boost/smart_ptr/detail/operator_bool.hpp>

    void swap(scoped_array & b) BOOST_NOEXCEPT
    {
        T * tmp = b.px;
        b.px = px;
        px = tmp;
    }
};

template<class T> inline void swap(scoped_array<T> & a, scoped_array<T> & b) BOOST_NOEXCEPT
{
    a.swap(b);
}

} // namespace boost

上面的原始碼和我之前分析過的scoped_ptr類似,只不過變成了對陣列的封裝。scoped_array解構函式中的check_delete實現是這樣的:

template<class T> inline void checked_array_delete(T * x)
{
    typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
    (void) sizeof(type_must_be_complete);
    delete [] x;
}

呵呵,這裡是delete[] x。

特點

scoped_array的主要特點如下:

  • 建構函式接受的指標p必須是new[]的結果,而不能是new表示式的結果。
  • 沒有*、->操作符過載,因為scoped_array持有的不是一個普通指標。
  • 解構函式使用delete []釋放資源,而不是delete。
  • 提供operator[]操作符過載,可以像普通陣列一樣用下標訪問元素。
  • 沒有begin()、end()等類似容器的迭代器操作函式

用法

sceopd_array與scoped_ptr源於相同的設計思想,故而用法非常相似:它只能在被生命的作用域內使用,不能拷貝、賦值。唯一不同的是scoped_array包裝的是new[]產生的指標,並在析構時呼叫delete[],因為它管理的是動態陣列,而不是單個物件

程式碼示例:

class test {
public:
    test() { cout<<"ctor"<<endl; }
    ~test() { cout<<"dtor"<<endl; }
};

int main()
{
    int *arr = new int[100];
    scoped_array<int> sa(arr);

    std::fill_n(&sa[0], 100, 5); 
    sa[10] = sa[20] + sa[30];

    cout<<sa[10]<<endl;

    scoped_array<test> ptest(new test[3]);

    return 0;
}

scoped_array過載了operator[],因此用起來就像是一個普通的陣列,但不能使用”首地址+N”的方式訪問元素,並且scoped_array不提供陣列索引的範圍檢查,如果使用超過動態陣列大小的索引或者是負數索引將引發未定義行為。

使用建議

scoped_array沒有給程式增加額外的負擔,它的速度與原始陣列同樣快。但scoped_array功能很有限,不能動態增長。也沒有迭代器支援,不能搭配STL演算法,僅有一個純粹的”裸”陣列介面。而且我們要儘量避免使用new[]操作符,儘量使用scoped_array,甚至是更好的std::vector。


參考:

  • Boost程式庫完全開發指南,作者:羅劍鋒

相關文章