2011年6月19日日曜日

shared_ptr を自分で書いてみる 4

次はカスタムデリータやアロケータを受け取るバージョンの コンストラクタ を実装してみます.(だんだんソース全部載せるのも長ったらしく感じてきたの で,今回は追加変更する部分だけコードを載せることにします)

まずカスタムデリータを渡す場合だと delete してるところを置き換えれ ばいいので,こんな感じに shared_count を派生すれば良さそうです.

template <typename T, typename D>
struct shared_count_with_deleter :public shared_count
{
    D deleter_;
    shared_count_with_deleter(D d) :deleter_(d) {}
    virtual void release(void* p) const
    {
        deleter_(reinterpret_cast<T*>(p));
    }
};

コンストラクタはこんな感じ.

template<typename Y, typename D> shared_ptr(Y * p, D d)
    :ptr_(p), count_(new shared_count_with_deleter<Y, D>(d))
{
}  

次にカスタムアロケータを渡す場合を考えてみます.やることは shared_count の生成破棄をアロケータ経由で行うようにすればいいという ことなので,コンストラクタの実装はカスタムアロケータで確保したメモリ に対して placement new で shared_count を生成します.

template<typename Y, typename D, typename A> shared_ptr(Y * p, D d, A a)
    :ptr_(p), count_(0)
{
    typedef shared_count_with_deleter_allocator<Y, D, A> sc_type;
    typename A::template rebind<sc_type>::other a2(a);
    count_ = a2.allocate(1);
    new (static_cast<void*>(count_)) sc_type(d, a);
}

placement new によって生成さたので shared_count 側で自分自身を破棄 するのに delete 演算子は使えません.というわけで自分自身を破棄する 処理をカスタム化するために destroy メソッドを追加します.

struct shared_count
{
    long count_;

    shared_count() :count_(1) {}
    virtual ~shared_count() {}
    
    void increment() { ++count_; }
    void decrement(void* p)
    {
        if (--count_ == 0) {
            release(p);
            destroy();
        }
    }
    virtual void destroy() { delete this; }
    virtual void release(void*) const = 0;
};

そしてアロケータ利用版の shared_count_with_deleter_allocator では destroy で明示的にデストラクタを呼び,アロケータ経由でメモリを開放 するようにします.

template <typename T, typename D, typename A>
struct shared_count_with_deleter_allocator :public shared_count
{
    D deleter_;
    A allocator_;

    typedef shared_count_with_deleter_allocator<T,D,A> this_type;
    
    shared_count_with_deleter_allocator(D d, A a) :deleter_(d), allocator_(a) {}
    virtual void release(void* p) const
    {
        deleter_(reinterpret_cast<T*>(p));
    }
    virtual void destroy()
    {
        typedef typename A::template rebind<this_type>::other A2;
        A2 a(allocator_);
        this->~this_type();
        a.deallocate(this, 1);
    }
};

いちおうちゃんと動いてるかなー?

0 件のコメント:

コメントを投稿