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より実装は簡単ですが、使用時に多少手間がかかります。
こちらを是非ご覧ください。

サンプル