2

トレーニングの目的で、私は独自のスマートポインターを作成しようとしていstd::shared_ptrます。メモリ内の特定のブロックを参照する共有ポインタがまだあるかどうかstatic std::map<void *, int> ref_trackを追跡する があります。

私のコンセプトはこれです:

template <typename PType>
class shared_ptr
{
    public:
        shared_ptr()
        : value_(nullptr), ptr_(nullptr)
        {}

        template <typename T>
        explicit shared_ptr(T * ptr)
        : shared_ptr()
        {
            reset(ptr);
        }

        template <typename T>
        shared_ptr(shared_ptr<T> const & other)
        : shared_ptr()
        {       
            reset(other.get());
        }

        ~shared_ptr()
        {
            reset();
        }

        void reset()
        {
            if(value_)
            {
                delete value_; // Segmentation fault here!
                value_ = 0;
                ptr_ = 0;
            }
        }

        template <typename T>
        void reset(T * ptr)
        {   
            reset();
            if(ptr)
            {
                value_ = new shared_ptr_internal::storage_impl<
                    T
                >(ptr);
                ptr_ = ptr;
            }
        }

        PType * get() const
        {
            return ptr_;
        }

        typename shared_ptr_internal::ptr_trait<PType>::type operator *()
        {
            return *ptr_;
        }

    private:
        shared_ptr_internal::storage_base * value_;
        PType * ptr_;
};

テスト スイートを実行しているときに、次のことに気付きました。

shared_ptr<int> a(new int(42));
a.reset(new int(13));

正常に動作しますが、

shared_ptr<int> a(new int(42));
a = shared_ptr<int>(new int(13));

は問題を引き起こします:*a0ではなく、 のデストラクタでセグメンテーション違反でクラッシュし13ます。ソース コードのクラッシュをコメントでマークしました。delete value_a

使用される内部クラスは次のとおりです。

namespace shared_ptr_internal
{

    typedef std::map<void *, int> ref_tracker;
    typedef std::map<void *, int>::iterator ref_tracker_iterator;
    typedef std::pair<void *, int> ref_tracker_entry;

    static ref_tracker ref_track;

    struct storage_base
    {
        virtual ~storage_base() {}
    };

    template <typename PType>
    struct storage_impl : storage_base
    {
        storage_impl(PType * ptr)
        : ptr_(ptr)
        {
            ref_tracker_iterator pos = ref_track.find(ptr);
            if(pos == ref_track.end())
            {
                ref_track.insert(
                    ref_tracker_entry(ptr, 1)
                );
            }
            else
            {
                ++pos->second;
            }
        }

        ~storage_impl()
        {
            ref_tracker_iterator pos = ref_track.find(ptr_);
            if(pos->second == 1)
            {
                ref_track.erase(pos);
                delete ptr_;
            }
            else
            {
                --pos->second;
            }
        }

        private:
            PType * ptr_;
    };

    template <typename PType>
    struct ptr_trait
    {
        typedef PType & type;
    };

    template <>
    struct ptr_trait<void>
    {
        typedef void type;
    };
}

大量のソース コードで申し訳ありませんが、さらに絞り込む方法がわかりません。セグメンテーション違反の原因となる可能性のあるアイデアと、手動でリセットを使用したときにこれが発生しない理由を教えていただければ幸いです。

アップデート

私の(機能していない)代入演算子:

template <typename T>
shared_ptr<PType> & operator =(shared_ptr<T> const & other)
{
    if(this != &other)
    {
        value_ = nullptr;
        ptr_ = nullptr;
        reset(other.get());
    }
    return *this;
}
4

3 に答える 3

2

代入演算子がありません。

これは、次のコードで次のことを意味します。

a = shared_ptr<int>(new int(13));

一時共有ポインタが作成されます。a次に、デフォルトの代入演算子は、古い値を解放したり、参照カウントを更新したりせずに、単にポインターをコピーします。次に、一時的に値を削除aし、ダングリングポインターを残します。

于 2012-07-02T11:08:04.330 に答える
2

3 つのルールに違反しているようです: カスタム コピー コンストラクターとカスタム デストラクタがありますが、カスタム代入演算子はありません。したがって、参照追跡を更新せずに、2 つのポインターを一時的なものからa = shared_ptr<int>(new int(13))コピーするだけです。したがって、テンポラリを破棄すると、そのポインタへの参照が追跡されなくなり、そのポインタが削除されます。また、代入で古いポインタがリークされることに注意してください。value_ptr_

于 2012-07-02T11:08:46.307 に答える
0

古いオブジェクトへの参照数を減らし、割り当てられたオブジェクトへの参照数を増やす代入演算子をポインタ クラスに追加するのを忘れていました。ほとんどの場合、copy d'tor と swap 関数に関して operator= を実装する最も簡単な方法です。

shared_ptr& shared_ptr<T>::operator=( shared_ptr<T> other ) 
{
    other.swap( this );

    return *this;
}
于 2012-07-02T11:11:11.393 に答える