2011年6月26日日曜日

shared_ptr を自分で書いてみる 5

shared_ptr といいつつ,今回は shared_ptr のお供であるところの weak_ptr を書いてみます.

weak_ptr というのは要するに所有権を持たない shared_ptr なので, 自分が参照している対象の所有者が 0 になったとき,自分も対象のポインタ を参照できなくなるようにします.

参照している所有者の数は shared_count クラスで管理しているわけです から, weak_ptr もこれを共有しつつ所有者数が 0 になっていないかを確 認すれば良いわけです.

しかし,現状の shared_count クラスでは,所有者の数が 0 になった瞬間 に自分自身を破棄しているので,これでは weak_ptr 側から所有者数を チェックする際に無効なインスタンスにアクセスしてしまうので具合がよろ しくありません.

ということで shared_count を次のように変更してみます. weak_ptr からの参照数を管理するようにし,所有数が 0 になったときではなく, weak_ptr からの参照数が 0 になったときに自分を破棄するようにしました.

struct shared_count
{
    long count_;
    long weak_count_;

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

ここまでくれば,あとは shared_ptr と同じ要領で weak_ptr が実装で きます. boost::weak_ptrintroduction にもあるとおり, weak_ptr 自身からは直接ポインタにアクセスせず shared_ptr に変換し てからアクセスするので,この変換するタイミングで所有者数をチェックす るようにします.

ということで weak_ptr から shared_ptr へ変換するコンストラクタは こんな具合で.

template <typename Y> shared_ptr(const weak_ptr<Y>& r)
    :ptr_(r.ptr_), count_(r.count_)
{
    if (!count_ || count_->count_ == 0) {
        throw bad_weak_ptr();
    }
    else {
        count_->increment();
    }
}

weak_ptr の実装はこんな感じに.

template<class T> class weak_ptr
{
    T* ptr_;
    shared_count* count_;

    template <typename Y> friend class shared_ptr;
    template <typename Y> friend class weak_ptr;
public:
    typedef T element_type;

    weak_ptr() :ptr_(0), count_(0) {}
    ~weak_ptr()
    {
        if (count_) count_->dec_weak_ref();
    }

    weak_ptr(const weak_ptr& r) :ptr_(r.ptr_), count_(r.count_)
    {
        if (count_) count_->inc_weak_ref();
    }
    template<class Y> weak_ptr(const weak_ptr<Y>& r) :ptr_(r.ptr_), count_(r.count_)
    {
        if (count_) count_->inc_weak_ref();
    }
    template<class Y> weak_ptr(const shared_ptr<Y>& r) :ptr_(r.ptr_), count_(r.count_)
    {
        if (count_) count_->inc_weak_ref();
    }

    weak_ptr& operator = (const weak_ptr& r)
    {
        if (count_ != r.count_) {
            weak_ptr(r).swap(*this);
        }
        return *this;
    }
    template<class Y> weak_ptr& operator = (const weak_ptr<Y>& r)
    {
        if (count_ != r.count_) {
            weak_ptr(r).swap(*this);
        }
        return *this;
    }
    template<class Y> weak_ptr& operator = (const shared_ptr<Y>& r)
    {
        if (count_ != r.count_) {
            weak_ptr(r).swap(*this);
        }
        return *this;
    }

    long use_count() const { return count_ ? count_->count_ : 0; }
    bool expired() const { return use_count() == 0; }
    shared_ptr<T> lock() const
    {
        return expired() ? shared_ptr<T>() : shared_ptr<T>(*this);
    }

    void reset()
    {
        weak_ptr().swap(*this);
    }
    void swap(weak_ptr<T>& r)
    {
        std::swap(ptr_, r.ptr_);
        std::swap(count_, r.count_);
    }
};

weak_ptr 自身ではポインタに触れないものの shared_ptr に変換する際 に必要になる兼ね合いでポインタをメンバに含んでいるのがちょっと勿体ないような気もしますね…

なにはともあれ,これでひとまず最低限それっぽい shared_ptr の機能は 揃って一区切りかなー? といった感じですね.

0 件のコメント:

コメントを投稿