shared_ptrの実装8 -weak_ptrに対応させる-

boost::weak_ptrの使い方は、多くの方々が解説していますので割愛します。


ここでは実装方法についてかなり適当に解説します。
一応前回(shared_ptrの実装7 - while( c++ );)の続き。
まとめページshared_ptrまとめ - while( c++ );)にshared_ptrのサンプルをアップしました。
とりあえず以下のよう扱い方が出来るweak_ptrを目指します。

使い方
class Hoge
{
public:
    void func(){}
};

int main()
{
    weak_ptr< Hoge > b;
    {
        shared_ptr< Hoge > a( new Hoge() );
        b = a;
    }
    if( shared_ptr< Hoge > temp = b.lock() )
    {
        temp->func();    //既にdelete済みなので実行されない
    }
    return 0;
}
weak_ptrの仕組み

weak_ptrに対応させるためには、参照カウンタクラス(shared_count、weak_count)によって共有される共有オブジェクトクラス(sp_counted_base)に、shared_ptr/weak_ptr用の2つのカウンタを持たせます。

class sp_counted_base
{
..略..
public:
    virtual void dispose() = 0;

public:
    sp_counted_base()
        : shared( 1 )
        , weak( 1 )
    {}

private:
    int shared;     ///<    shared_ptr用参照カウンタ
    int weak;       ///<    weak_ptr用参照カウンタ

};
template < typename T >
class sp_counted_impl_p
    : public sp_counted_base
{
public:
    void dispose()
    {
        delete ptr;
    }
public:
    sp_counted_impl_p( T* ptr )
        : ptr( ptr )
    {}
private:
    T* ptr;
};

template < typename T, typename D >
class sp_counted_impl_pd
    : public sp_counted_base
{
public:
    void dispose()
    {
        del( ptr );
    }

public:
    sp_counted_impl_pd( T* ptr, D del )
        : ptr( ptr )
        , del( del )
    {}

private:
    T* ptr;
    D del;
};

使い方の例のように適当なクラスをshared_ptrに格納すると、デフォルトでsp_counted_impl_pのインスタンスが生成され、shared==1、weak==1となります。

class Hoge
{
public:
    void func(){}
};

int main()
{
    shared_ptr< Hoge > p( new Hoge() );
    return 0;
}

ここでshared_ptrをshared_ptrにコピーすると、sp_counted_impl_pのインスタンスが共有され、sharedの値のみインクリメントされます。

    shared_ptr< Hoge > p( new Hoge() );
    shared_ptr< Hoge > q( p );


同様にshared_ptrをweak_ptrにコピーすると、sp_counted_impl_pのインスタンスが共有され、weakの値のみインクリメントされます。

    shared_ptr< Hoge > p( new Hoge() );
    weak_ptr< Hoge > q( p );


以下のようにshared_ptrのデストラクタが先に呼ばれてsharedがデクリメント、インスタンスがdeleteされると、weakもデクリメントされます。この時weak==0になればsp_counted_impl_pもdeleteされます。今回の例ではweak==1なので、sp_counted_impl_pはまだdeleteされません。

    weak_ptr< Hoge > q;
    {
        shared_ptr< Hoge > p( new Hoge() );
        q = p;
    }←ここ

weak_ptrが消滅すると、weakのみデクリメントされます。weak==0であれば、sp_counted_impl_pもdeleteされます。

{
    weak_ptr< Hoge > q;
    {
        shared_ptr< Hoge > p( new Hoge() );
        q = p;
    }
}←ここ

weak_ptrが指すインスタンスが既にdeleteされている可能性がある場合、lockすることで安全にアクセスできます。lock時にshared==0であれば、nullptrを指すshared_ptrが返されます。

{
    weak_ptr< Hoge > q;
    {
        shared_ptr< Hoge > p( new Hoge() );
        q = p;
    }
    if( shared_ptr< Hoge > temp = q.lock() )
    {
        temp->func();//呼ばれない
    }
}

  • インスタンスを渡してshared_ptrを生成するとshared=1、weak=1
  • shared_ptrをコピーするとsharedのみインクリメント
  • shared_ptrが破棄される場合、sharedのみデクリメント
    • この時shared==0になれば、インスタンスをdelete
      • さらにweakもデクリメントし、weak==0になれば、sp_counted_impl_pもdelete
  • weak_ptrをコピーするとweakのみインクリメント
  • weak_ptrが破棄される場合、weakのみデクリメント
    • この時weak==0になれば、sp_counted_impl_pもdelete

以上のことを踏まえて、次回weak_ptrを実装します。