2011年6月16日木曜日

続々・スマートポインタの問題…というよりも

前回の続きになります。

そもそも、スマートポインタの目的とは、new したものを delete し忘れないようにするためのものでした。今実現したいのは、クラス内から this を安全に外部へ渡したいということで、スマートポインタとは目的が違うような気がします。

つまり、shared_ptr を必要としない weak_ptr があればいいのでは?ということで
// ポインタ情報
template <class T>
class distributable_data
{
public :
    distributable_data( T* ptr_ )
      : ptr( ptr_ ),
        count( 1 )
    {
    }

    long use_count( void ) const
    {
        return this->count;
    }

    void reference( void )
    {
        this->count++;
    }

    T* get( void )
    {
        return this->ptr;
    }

    const T* get( void ) const
    {
        return this->ptr;
    }

    void clear( void )
    {
        this->ptr = 0;
    }

    static bool dereference( distributable_data<T>* data )
    {
        data->count--;
        if( data->use_count() == 0 )
        {
            delete data;
            return false;
        }
        else
        {
            return true;
        }
    }

private :
    T* ptr;
    long count; // ポインタ先ではなく、このクラスの参照数
};
// 配布されるポインタ
template <class T>
class distributed_ptr
{
public :
    distributed_ptr( distributable_data<T>* data_ )
      : data( data_ )
    {
        this->data->reference();
    }

    distributed_ptr( const distributed_ptr<T>& org )
      : data( org.data )
    {
        this->data->reference();
    }

    ~distributed_ptr( void )
    {
        distributable_data<T>::dereference( this->data );
    }

    bool expire( void ) const
    {
        return this->data->get() == 0;
    }

    T* operator->( void )
    {
        return this->data->get();
    }

    const T* operator->( void ) const
    {
        return this->data->get();
    }

private :
    distributable_data<T>* data;
};
// 配布可能ポインタ
template <class T>
class distributable_ptr
{
public :
    distributable_ptr( void )
    {
        this->data = new distributable_data<T>( dynamic_cast<T*>(this) );
    }

    virtual ~distributable_ptr( void )
    {
        if( distributable_data<T>::dereference( this->data ) )
        {
            this->data->clear();
        }
    }

    distributed_ptr<T> lock( void )
    {
        return distributed_ptr<T>( this->data );
    }

private :
    distributable_data<T>* data;
};
こんな感じかな?
※まだ動作検証してません。

で、使うときは、こんな感じに継承して使います。わざわざ継承させるのは、クラスが破棄されたときにコピーされたポインタを無効化するためです。
class Hoge : public distributable_ptr<Hoge>
{
public :
    Hoge( void )
      : distributable_ptr<Hoge>()
    {
    }

    virtual ~Hoge( void )
    {
    }

    void Func( void )
    {
    }
};

void Foo( void )
{
    Hoge  hoge[ 10 ];
    // ポインタ取得
    distributed_ptr<Hoge> ptr = hoge[2].lock();
    // 使用可能か確認してからアクセス
    if( !ptr.expire() )
        ptr->Func();
}
とりあえず、スタック上に配列で確保してみましたが、もちろん new で構築しても問題ありませんし、そのポインタを shared_ptr で管理していても全く問題ないはずです。

distributable_data を構築するのは最初に lock() 関数が呼ばれたときにした方がとか、distributed_ptr のコピー処理とか、スレッドセーフとかも考えるともっといろいろ工夫が必要ですが、とりあえずこんな感じで。しかし、そして、さらに野望が...続く

0 件のコメント:

コメントを投稿