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
- この時shared==0になれば、インスタンスをdelete
- weak_ptrをコピーするとweakのみインクリメント
- weak_ptrが破棄される場合、weakのみデクリメント
- この時weak==0になれば、sp_counted_impl_pもdelete
- この場合sharedは既に0になっており、インスタンスもdelete済み
- この時weak==0になれば、sp_counted_impl_pもdelete
以上のことを踏まえて、次回weak_ptrを実装します。