shared_ptrの実装9 -weak_ptrの実装-

前回(shared_ptrの実装8 -weak_ptrに対応させる- - while( c++ );)の続き。

weak_ptrを作ります。

sp_counted_base

リソースを管理するための参照数(shared、weak)を保持するクラス。shared_count、weak_countによって共有される共有オブジェクト。

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

    virtual ~sp_counted_base(){}

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

        //コピー禁止
    private:
        sp_counted_base( sp_counted_base const& );
        sp_counted_base& operator=( sp_counted_base const& );
};
sharedの上げ下げ
  • addref
    • リソースがdeleteされていなければ、sharedのincrement。
  • release
    • shared==0になったらdispose()でリソースを破棄し、weak_release()でweakをdecrementする。
  • dispose
    • 派生クラスsp_counted_impl_p、sp_counted_impl_pdで実装。
class sp_counted_base
{
    //shared
public:
    bool addref()
    {
        if( shared == 0 )return false;

        shared ++;
        return true;
    }

    void release()
    {
        if( -- shared == 0 )
        {
            dispose();
            weak_release();
        }
    }

    virtual void dispose() = 0;
..略..
};
weakの上げ下げ
  • weak_addref
    • weakのincrement
  • weak_release
    • weakをdecrementして0になったら自身をdeleteする。
class sp_counted_base
{
..略..

    //weak
public:
    void weak_addref()
    {
        weak ++;
    }

    void weak_release()
    {
        if( -- weak == 0 )
        {
            destroy();
        }
    }

    void destroy()
    {
        delete this;
    }

..略..
};

sp_counted_impl

参照カウンタの上げ下げと、リソースの所有権を持つsp_counted_base派生クラス。

sp_counted_impl_p

リソースの保持とdeleteを行う。

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;
};
sp_counted_impl_pd

custom deleterによるリソースのdelete。

    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_count

weak_countからshared_countを生成するためのコンストラクタを追加。

class shared_count
{
    friend class weak_count;
private:
    sp_counted_base* impl;

..略..

public:
    explicit shared_count( weak_count const& c );
    shared_count( weak_count const& c, no_throw_tag );
};

class weak_count{ ... };

inline shared_count::shared_count( weak_count const& c )
    : impl( c.impl )
{
    if( impl == nullptr || !impl->addref() )
    {
        throw bad_weak_ptr();
    }
}

inline shared_count::shared_count( weak_count const& c, no_throw_tag )
    : impl( c.impl )
{
    if( impl != nullptr && !impl->addref() )
    {
        impl = nullptr;
    }
}

weak_count

weak_ptrが保持する参照カウンタクラス。shared_ptrとsp_counted_base*を共有している。
shared_ptrが持つshared_countから生成される。

  • コンストラク
    • weak_addref
  • デストラク
    • weak_release。
  • operator=
    • コピー元sp_counted_baseのweak_release
    • コピー先sp_counted_baseのweak_addref
class weak_count
{
    friend class shared_count;
private:
    sp_counted_base* impl;

public:
    weak_count()
        : impl( nullptr )
    {}

    weak_count( shared_count const& c )
        : impl( c.impl )
    {
        if( impl != nullptr )
            impl->weak_addref();
    }

    weak_count( weak_count const& c )
        : impl( c.impl )
    {
        if( impl != nullptr )
            impl->weak_addref();
    }

    ~weak_count()
    {
        if( impl != nullptr )
            impl->weak_release();
    }

public:
    bool empty()const
    {
        return impl == nullptr;
    }

public:
    weak_count& operator=( shared_count const& c )
    {
        sp_counted_base* temp = c.impl;
        if( temp != impl )
        {
            if( temp )temp->weak_addref();
            if( impl )impl->weak_release();
            impl = temp;
        }
        return *this;
    }

    weak_count& operator=( weak_count const& c )
    {
        sp_counted_base* temp = c.impl;
        if( temp != impl )
        {
            if( temp )temp->weak_addref();
            if( impl )impl->weak_release();
            impl = temp;
        }
        return *this;
    }
};

weak_ptr

  • lock
    • リソースが既にdeleteされていればnullptrを指すshared_ptr、まだ生きていればsp_counted_baseを共有するshared_ptrを返す。
  • operator*、operator->
template< typename Type >
class weak_ptr
{
public:
    typedef typename shared_ptr_traits< Type >::reference_t reference_t;

public:
    weak_ptr()
        : ptr( nullptr )
        , count()
    {}

    template< typename Y >
    weak_ptr( weak_ptr< Y > const& p )
        : ptr( p.lock().get() )
        , count( p.count )
    {
    }

    template< typename Y >
    weak_ptr( shared_ptr< Y > const& p )
        : ptr( p.ptr )
        , count( p.count )
    {
    }

public:
    template< typename Y >
    weak_ptr& operator=( weak_ptr< Y > const& p )
    {
        ptr = p.lock().get();
        count = p.count;
        return *this;
    }

    template< typename Y >
    weak_ptr& operator=( shared_ptr< Y > const& p )
    {
        ptr = p.ptr;
        count = p.count;
        return *this;
    }

public:
    shared_ptr< Type > lock()const
    {
        return shared_ptr< Type >( *this, no_throw_tag() );
    }

public:
    reference_t operator*()const
    {
        if( ptr == nullptr )throw nullptr_exception();
        return *ptr;
    }

    Type* operator->()const
    {
        if( ptr == nullptr )throw nullptr_exception();
        return ptr;
    }


private:
    Type* ptr;
    weak_count count;
};

test

  • shared_ptr02.zip 直
    • VC++2008EEで動作確認
    • boostとの違い
      • namespaceはhogeとしています
      • sp_counted_baseをshared_object_baseに変更
      • sp_counted_impl_pをshared_object_implに変更
      • sp_counted_impl_pdをshared_object_impl_delに変更
#include <iostream>
#include "smart_ptr/shared_ptr.h"
#include "smart_ptr/weak_ptr.h"

#include <boost/smart_ptr.hpp>

class Hoge
{
public:
    void func(){ std::cout << "hoge" << std::endl; }
};

int main()
{
    //メモリリークの検出
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

    {
        hoge::weak_ptr< Hoge > q;
        {
            hoge::shared_ptr< Hoge > p( new Hoge() );
            q = p;
        }
        if( hoge::shared_ptr< Hoge > temp = q.lock() )
            temp->func();
    }

    return 0;
}

とりあえず、期待通りに動作してるようです。
足りない機能は今後追加予定。