切実にほしかった参照カウント付きスマートポインタ
とりあえず完成です
いつも勝手にお世話になってるmeltさんのとこからもらってきたのを
boost::shared_ptr風にして機能拡張しました。
#include <functional> // less namespace shand { namespace detail { // shared_ptr<void>を作成するために参照型作成 template<class T> struct shared_ptr_traits { typedef T& reference; }; template<> struct shared_ptr_traits<void> { typedef void reference; }; #if !defined(_MSC_VER) || (_MSC_VER > 1300) // 1300 == VC++ 7.0 template<> struct shared_ptr_traits<void const> { typedef void reference; }; template<> struct shared_ptr_traits<void volatile> { typedef void reference; }; template<> struct shared_ptr_traits<void const volatile> { typedef void reference; }; #endif // カスタム削除子 template <typename Type> class shared_deleter_base { public: shared_deleter_base() {} virtual ~shared_deleter_base() {} virtual void destroy() = 0; }; template <typename Type, typename Deleter> class shared_deleter : public shared_deleter_base<Type> { Type* object_; Deleter deleter_; public: shared_deleter(Type *object, Deleter deleter) : object_(object), deleter_(deleter) {} virtual ~shared_deleter() {} virtual void destroy() { deleter_(object_); } }; } // namespace detail template <class Type> class shared_ptr { Type* object_; // ポインタ int* counter_; // 参照カウンタ detail::shared_deleter_base<Type>* deleter_; // カスタム削除子 public: typedef Type element_type; typedef Type value_type; typedef Type* pointer; typedef typename detail::shared_ptr_traits<Type>::reference reference; shared_ptr() : object_(0), counter_(new int(1)), deleter_(0) {} shared_ptr(const shared_ptr& src) : object_(0), counter_(0), deleter_(0) { set(src); } explicit shared_ptr(Type* object) : object_(object), counter_(new int(1)), deleter_(0) {} // 削除子指定のコンストラクタ template <typename Deleter> shared_ptr(Type* object, Deleter deleter) : object_(object), counter_(new int(1)), deleter_(0) { try { deleter_ = new detail::shared_deleter<Type, Deleter>(object_, deleter); } catch(...) { deleter(object_); delete counter_; throw; } } ~shared_ptr() { release(); } // ポインタの取得 Type* get() const { return object_; } // 参照カウント取得 long use_count() const { if (!counter_) return 0; return *counter_; } // 参照先が1つか判断 bool unique() const { return use_count() == 1; } shared_ptr& operator=(const shared_ptr& rhs) { set(rhs); return *this; } Type* operator->() const { return get(); } reference operator*() const { return *get(); } bool operator!() const { return object_ == 0; } operator bool() const { return object_ != 0; } private: void set(const shared_ptr& src) { if (this != &src) { // 解放して新しいスマートポインタを作成 release(); object_ = src.object_; counter_ = src.counter_; deleter_ = src.deleter_; // 参照カウントを増やす if (counter_ != 0) ++*counter_; } } void release() { if (counter_ != 0 && --*counter_ == 0) { if (deleter_) { deleter_->destroy(); delete deleter_; } else { delete object_; } delete counter_; } object_ = 0; counter_ = 0; deleter_ = 0; } }; template<class Type> inline bool operator==(shared_ptr<Type> const & lhs, shared_ptr<Type> const & rhs) { return lhs.get() == rhs.get(); } template<class Type> inline bool operator!=(shared_ptr<Type> const & lhs, shared_ptr<Type> const & rhs) { return lhs.get() != rhs.get(); } // map格納用 template<class Type> inline bool operator<(shared_ptr<Type> const & lhs, shared_ptr<Type> const & rhs) { return std::less<Type*>()(lhs.get(), rhs.get()); } } // namespace shand
サンプル1
#include <iostream> #include <shand/shared_ptr.hpp> using namespace std; using namespace shand; void disp(shared_ptr<int> p) { cout << *p << endl; } // ←参照カウントを使用しているのでここではdeleteされない int main() { shared_ptr<int> p(new int(3)); // スマートポインタを値渡し disp(p); return 0; // ←ここでdeleteされる }
サンプル2
#include <string> #include <shand/shared_ptr.hpp> using namespace std; using namespace shand; int main() { // カスタム削除子(fclose())を指定 shared_ptr<FILE> p(fopen("c:/a.txt", "wb"), fclose); string str = "abc\r\n"; fwrite(str.c_str(), str.length(), 1, p.get()); return 0; // ←ここでfcloseされる }
サンプル2.ex(検証用...本当はもっと細かいけどとりあえず版)
#include <iostream> #include <string> #include <shand/shared_ptr.hpp> using namespace std; using namespace shand; FILE* fopen_ex(const char *file_name, const char *mode) { cout << "fopen" << endl; return fopen(file_name, mode); } int fclose_ex(FILE *fp) { cout << "fclose" << endl; return fclose(fp); } int main() { shared_ptr<FILE> p(fopen_ex("c:/a.txt", "wb"), fclose_ex); string str = "abc\r\n"; fwrite(str.c_str(), str.length(), 1, p.get()); return 0; }
【できないこと】
・キャスト可能なshared_ptr同士の代入
・リセット
・スワップ
【本家】
http://boost.cppll.jp/HEAD/libs/smart_ptr/shared_ptr.htm
【参考URL】
http://www.kmonos.net/alang/boost/classes/shared_ptr.html
http://gimite.net/behind/boostsample.htm
# 2007/07/18更新
・カスタム削除子作成時の例外処理追加
・shared_ptr
# 2007/10/24更新
・カスタム削除子作成時にポインタを渡すよう修正