shared_ptrの実装6

ここまでのまとめ

  • shared_ptrでは参照カウンタの管理、インスタンスの管理が完全に隠蔽されている。
  • 可能なこと
    • shared_ptr< void >
    • pimplイディオム(不完全型クラスを使った宣言)
    • 暗黙のアップキャスト
    • deleterによる削除
  • 未実装
    • weak_ptrへの変換
  • 適当なクラス図
    • JUDEを使いました


shared_ptrクラス

//インスタンスの参照を返すための特性
template < typename T >
struct shared_ptr_traits
{
    typedef T& reference;
};

//shared_ptr< void >の特殊化
template<>
struct shared_ptr_traits< void >
{
    typedef void reference;
};


//参照カウンタを共有するスマートポインタ
template < typename T >
class shared_ptr
{
public:
    //operato*の戻り値の型
    typedef typename shared_ptr_traits< T >::reference reference;

    //任意のshared_ptrにプライベートメンバを公開
    template < typename U > friend class shared_ptr;

private:
    T* ptr;                //インスタンスを指すポインタ。所有権はない。
    shared_count count;    //参照カウンタ、インスタンスの管理クラス

public:
    shared_ptr()
        : ptr( 0 )
        , count()
    {}

    //以下のような生成のためのコンストラクタ
    //    shared_ptr< T > p( new T );
    //    shared_ptr< T > p( new U );  //T <- U
    //    shared_ptr< void > p( new T );
    //    shared_ptr< int >  p( new int );
    //    shared_ptr< void > p( new int );
    template < typename U >
    explicit shared_ptr( U* ptr )
        : ptr( ptr )
        , count( ptr )
    {}

    //上記のコピーコンストラクタ
    template < typename U >
    shared_ptr( shared_ptr< U > const& sp )
        : ptr( sp.ptr )
        , count( sp.count )
    {}

    //deleterを指定できるコンストラクタ
    //    void HogeDeleter( Hoge* p ){ delete p; }
    //    struct PiyoDeleter{ void operator()( Piyo* p ){ delete p; } };
    //
    //    shared_ptr< Hoge > p( new Hoge, HogeDeleter );
    //    shared_ptr< Piyo > p( new Piyo, PiyoDeleter() );
    template < typename U, typename DeleterT >
    explicit shared_ptr( U* ptr, DeleterT deleter )
        : ptr( ptr )
        , count( ptr, deleter )
    {}

public:
    reference operator*()const
    {
        return *ptr;
    }

    T* operator->()const
    {
        return ptr;
    }
};

shared_countクラス

//参照カウンタ、インスタンスの管理
class shared_count
{
private:
    sp_counted_base* pimpl;    //共有オブジェクトのインスタンス

public:
    shared_count()
        : pimpl( 0 )
    {}

    //新規登録。参照数=1
    template < typename T >
    explicit shared_count( T* ptr )
        : pimpl( new sp_counted_impl< T >( ptr ) )
    {}

    //新規登録(deleterによる削除)。参照数=1
    template < typename T, typename DeleterT >
    explicit shared_count( T* ptr, DeleterT deleter )
        : pimpl( new sp_counted_impl_del< T, DeleterT >( ptr, deleter ) )
    {}

    //参照数のデクリメント。参照数=0の時delete
    ~shared_count()
    {
        if( pimpl )pimpl->release();
    }

public:
    //参照数のインクリメント
    shared_count( shared_count const& sc )
        : pimpl( sc.pimpl )
    {
        if( pimpl )pimpl->addref();
    }

    //既存のポインタの参照数をデクリメント、コピー元をインクリメント
    shared_count& operator=( shared_count const& sc )
    {
        sp_counted_base* temp = sc.pimpl;
        if( temp != pimpl )
        {
            if( temp )temp->addref();
            if( pimpl )pimpl->release();
            pimpl = temp;
        }
        return *this;
    }
};

sp_counted_baseクラス

//参照カウンタの共有オブジェクト基底クラス。参照数の管理のみ
class sp_counted_base
{
private:
    long use_count;    //参照数

public:
    sp_counted_base()
        : use_count( 1 )
    {}

    virtual ~sp_counted_base()
    {}

public:
    void addref()
    {
        use_count ++;
    }

    void release()
    {
        if( -- use_count == 0 )
        {
            dispose();
            delete this;
        }
    }

public:
    //インスタンスの破棄。派生クラスで実装
    virtual void dispose() = 0;
};

sp_counted_implクラス

//参照カウンタの共有オブジェクト実装クラス。インスタンスの管理
template < typename T >
class sp_counted_impl
    : public sp_counted_base
{
private:
    T* ptr;         //インスタンスの指すポインタ。所有権あり

public:
    sp_counted_impl( T* ptr )
        : ptr( ptr )
    {}

public:
    void dispose()
    {
        delete ptr;
    }
};

sp_counted_impl_delクラス

//参照カウンタの共有オブジェクト実装クラス(deleter付き)。インスタンスの管理
template < typename T, typename DeleterT >
class sp_counted_impl_del
    : public sp_counted_base
{
private:
    T* ptr;         //インスタンスの指すポインタ。所有権あり
    DeleterT deleter;

public:
    sp_counted_impl_del( T* ptr, DeleterT deleter )
        : ptr( ptr )
        , deleter( deleter )
    {}

public:
    void dispose()
    {
        deleter( ptr );
    }
};