shared_ptrの実装7

deleter

前回のまとめ(shared_ptrの実装6 - while( c++ );)でいきなり出てきたdeleterによる削除について。

例えば、あるクラスの生成と破棄を行うライブラリがある時、

class IHoge{ ... };
IHoge* Create();
void Delete( Hoge* p );
class Hoge : public Hoge{ ... };
IHoge* Create()
{
    return new Hoge();
}
void Delete( IHoge* p )
{
    delete p;
}

単純なスマートポインタではこのようなオブジェクトを管理することができません。

  • 利用者はCreate()、Delete()の実装方法がわからないので勝手にdelete hoge;してはいけない
int main()
{
    IHoge* hoge = Create();
    hoge->...;
    Delete( hoge );    
    return 0;
}

shared_ptrの実装5 - while( c++ );のshared_ptrでは勝手にdeleteが実行されるので、バグになる可能性があります。

int main()
{
    shared_ptr< IHoge > hoge( Create() );
    hoge->...;
    return 0;
}//←ここでDelete()が呼ばれない

そこで、shared_ptrから参照されなくなったときにdeleteではなく、void Delete( T* )が呼ばれるようにしましょう、というのが今回のお題です。


boost風にするなら、コンストラクタでDeleteを渡す形がいいですかね。

int main()
{
    shared_ptr< IHoge > hoge( Create(), Delete );
    hoge->...;
    return 0;
}//←ここでDelete()を呼んでほしい

現在の仕様ではsp_counted_base内で参照数が0になったときdispose()が呼ばれるようになっています。

class sp_counted_base
{
...

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

public:
    virtual void dispose() = 0;
...
};

生のポインタが渡されると、disopseの実装クラスsp_counted_implでポインタが保持され、deleteが呼ばれます。

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;
    }
};

つまり、deleteの代わりにDelete( T* )を実行するためには、新たにdispose実装クラスを用意すればよいわけです。

template < typename T, typename DeleterT >
class sp_counted_impl_del
    : public sp_counted_base
{
private:
    T* ptr;         //インスタンスの指すポインタ。所有権あり
    DeleterT deleter;//ptrを削除するための関数、または関数オブジェクトを保持する

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

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

あとはshared_countとshared_ptrにコンストラクタを追加すれば完成です。

class shared_count
{
...

    template < typename T, typename DeleterT >
    explicit shared_count( T* ptr, DeleterT deleter )
        : pimpl( new sp_counted_impl< T, DeleterT >( ptr, deleter ) )
    {}
...
};
template < typename T >
class shared_ptr
{
...

    template < typename U, typename DeleterT >
    explicit shared_ptr( U* ptr, DeleterT deleter )
        : ptr( ptr )
        , count( ptr, deleter )
    {}

...
};


以上でdelete以外のインスタンス削除が可能になりました。

int main()
{
    shared_ptr< IHoge > hoge( Create(), Delete );
    hoge->...;
    return 0;
}//←ここでDelete()が呼ばれる

関数オブジェクトを渡すことも可能です。

int main()
{
    struct Deleter
    {
        void operator()( IPiyo* piyo )
        {
            piyo->Release(); //delete this;
        }
    };
    shared_ptr< IPiyo > piyo( Create(), Deleter() );
    piyo->...;
    return 0;
}//←ここでoperator()が呼ばれる