intrusive_ptrの実装。
最近、個人的にはshared_ptrより使用頻度の高いintrusive_ptrを実装してみます。参照先のオブジェクト側で参照カウンタを管理するスマートポインタですね。
intrusive_ptrが実体化、コピーされるときにintrusive_ptr_add_ref(T*)、破棄されるときにintrusive_release(T*)が呼ばれます。
参考サイト
使い方
参照カウンタは自分で用意します。
class hoge { public: void func() { std::cout << "hoge" << std::endl; } public: hoge() : ref_count( 0 ) {} private: int ref_count; friend void intrusive_ptr_add_ref( hoge* p ); friend void intrusive_ptr_release( hoge* p ); };
intrusive_ptr_add_refで参照カウンタのインクリメント、intrusive_ptr_releaseで参照カウンタをデクリメントし0になったらdeleteします。
void intrusive_ptr_add_ref( hoge* p ) { p->ref_count ++; } void intrusive_ptr_release( hoge* p ) { p->ref_count --; if( p->ref_count <= 0 ) delete p; }
int main() { intrusive_ptr< hoge > p( new hoge() ); p->func(); intrusive_ptr< hoge > q = p; q->func(); return 0; }
実装
コンストラクタでintrusive_ptr_add_ref、デストラクタでintrusive_ptr_releaseを呼び出します。
template< typename T > class intrusive_ptr { public: intrusive_ptr() : p( 0 ) {} intrusive_ptr( T* p, bool add = true ) : p( p ) { if( ( p != 0 ) && add ) intrusive_ptr_add_ref( p ); } template< typename U > intrusive_ptr( intrusive_ptr< U > const& rhs ) : p( rhs.get() ) { if( p != 0 ) intrusive_ptr_add_ref( p ); } intrusive_ptr( intrusive_ptr const& rhs ) : p( rhs.p ) { if( p != 0 ) intrusive_ptr_add_ref( p ); } ~intrusive_ptr() { if( p != 0 ) intrusive_ptr_release( p ); } private: T* p; };
代入演算子はCopy-and-swapイディオムを使用します。
template< typename T > class intrusive_ptr { /*略*/ public: void swap( intrusive_ptr& rhs ) { std::swap( p, rhs.p ); } template< typename U > intrusive_ptr& operator=( intrusive_ptr< U > const& rhs ) { intrusive_ptr( rhs ).swap( *this ); return *this; } intrusive_ptr& operator=( intrusive_ptr const& rhs ) { intrusive_ptr( rhs ).swap( *this ); return *this; } intrusive_ptr& operator=( T* rhs ) { intrusive_ptr( rhs ).swap( *this ); return *this; } };
あとは、ポインタとして振る舞うための->演算子などをオーバーロードします。
template< typename T > class intrusive_ptr { /*略*/ public: T* get()const{ return p; } T& operator*()const{ if( p == 0 )throw nullptr_exception(); return *p; } T* operator->()const{ if( p == 0 )throw nullptr_exception(); return p; } operator bool()const{ return p != 0; } bool operator!()const{ return p == 0; } bool operator<( const intrusive_ptr& r )const{ return p < r.p; } };
nullptrとnullptr_exception
ついでにnullptrも用意します。
#include <exception> //ヌルポインタを参照した場合の例外 class nullptr_exception : public std::exception { }; //ヌルポインタを表す const struct nullptr_t { //任意の型のポインタへの変換 template< typename Type > operator Type*()const{ return 0; } }nullptr;
template< typename T > class intrusive_ptr { /*略*/ //nullptrとの互換 public: intrusive_ptr( nullptr_t const& ) : p( nullptr ) { } intrusive_ptr& operator=( nullptr_t const& ) { intrusive_ptr( nullptr ).swap( *this ); return *this; } bool operator==( nullptr_t const& )const { return p == nullptr; } bool operator!=( nullptr_t const& )const { return p != nullptr; } };
intrusive_ptrはshared_ptrより実装は簡単ですが、使用時に多少手間がかかります。
こちらを是非ご覧ください。
サンプル
- intrusive_ptr.zip
- VC++2008EE用