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_ptr
の introduction にもあるとおり,
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
の機能は
揃って一区切りかなー? といった感じですね.