読者です 読者をやめる 読者になる 読者になる

shared_ptr

切実にほしかった参照カウント付きスマートポインタ

とりあえず完成です


いつも勝手にお世話になってる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更新

・カスタム削除子作成時にポインタを渡すよう修正



ライブラリまとめ