4

ブースト shared_ptr クラスの基本的な設計を理解しようとしています。それを fortran に "移植" したい (聞かないでください)。私が理解していることの 1 つは、参照カウントが shared_count クラスによって保持されていることです。これは私に質問を促します。私は長い間 C++ を使用しておらず、boost も使用したことがありません。

クラス X の 1 つのインスタンスを割り当て、それを 2 つの異なる shared_ptr インスタンスに渡すとします。私が理解していることから、各 shared_ptr インスタンスは他のインスタンスについて何も知らないため、refcount 1 を維持しながら、両方の shared_ptr インスタンスが同じ X インスタンスを参照しています。 X オブジェクトは削除され (refcount がゼロになると)、残りの shared_ptr にはダングリング ポインターが含まれます。shared_ptr refcount を保持するには、別の shared_ptr から shared_ptr を作成する必要があります。

私は正しいですか?そうでない場合、 shared_ptrs を介して参照されているという事実について何も知らないクラスを参照している shared_ptrs を追跡するにはどうすればよいですか?

4

2 に答える 2

7

基本的にあなたは正しいです。boost::enable_shared_from_thisあなたの例はダングリングポインターになります(基本クラスとして使用する場合、いくつかの例外があることに注意してください)。

説明

問題

boost:shared_ptr同じアイデアをstd::shared_ptr共有します。生のポインターからの参照カウントを使用してスマートポインターを作成します。ただし、すべてのスマート ポインターに共通の問題もあります。他のスマート ポインターに関連付けられていない別のスマート ポインターで生のポインターを使用すると、ダングリング ポインターと の複数回の呼び出しで終了しますdelete

int * ptr = new int;
{
    std::shared_ptr<int> shared1(ptr); // initialise a new ref_count = 1
    {
        std::shared_ptr<int> shared2(ptr);  // initialise a new ref_count = 1
    } // first call of delete, since shared2.use_count() == 0
} // second call of delete, since shared1.use_count() == 0. ooops

"解決"

オブジェクトへSの生のポインターから最初のスマート ポインターを作成した後は、 からの派生物でない限り、ではなく でのみコピー コンストラクターを使用する必要があります。ブーストにはこれと多少同等のものがありますが、生のポインターとスマートポインターを混在させることは依然として悪い考えです。さらに良い-スマートポインターを使用する場合は、生のポインターを使用しないでください。pOSpOstd::enable_shared_from_this

std::shared_ptr<int> ptr(new int);
{
    std::shared_ptr<int> shared1(ptr); // ptr.use_count() == 2
    {
        std::shared_ptr<int> shared2(ptr);  // ptr.use_count()  = 3
    } // ptr.use_count()  = 2
}  // ptr.use_count()  = 1

さらに良いことに、メモリを自分で割り当てるのではなく、std::make_sharedorを使用しboost:make_sharedます。

std::shared_ptr<int> ptr = std::make_shared<int>();
{
    std::shared_ptr<int> shared1(ptr); // ptr.use_count() == 2
    {
        std::shared_ptr<int> shared2(ptr);  // ptr.use_count() == 3
    } // ptr.use_count() == 2
}  // ptr.use_count() == 1

可能な実装

次の実装は、 と をサポートしstd::shared_ptrていないため、 に比べて非常に粗雑です。ただし、共有ポインターを処理する方法の概要を説明する必要があります。std::weak_ptrstd::enable_shared_from_this

//!\brief Base clase for reference counter
class reference_base{
    reference_base(const reference_base&);                            // not copyable
    reference_base& operator=(const reference_base &){return *this;}// not assignable    

protected:
    size_t ref_count; //!< reference counter
    virtual void dispose() = 0; //!< pure virtual
public:    
    //! initialize with a single reference count
    reference_base() : ref_count(1){}

    //! returns the current count of references
    size_t use_count() const{
        return ref_count;
    }

    //! increases the current count of references
    void increase(){
        ref_count++;
    }

    //! decreases the current count of references and dispose if the counter drops to zero
    void decrease(){
        if(--ref_count == 0)
            dispose();
    }
};

//! \brief Specialized version for pointer
template <class T>
class reference_base_ptr : public reference_base{
    typedef T* pointer_type;
protected:
    //! uses delete to deallocate memory
    virtual void dispose(){
        delete ptr;
        ptr = 0;
    }
public:
    reference_base_ptr(T * ptr) : ptr(ptr){}
    pointer_type ptr;
};

//! \brief Specialized version for arrays
template <class T>
class reference_base_range : public reference_base{
    typedef T* pointer_type;

protected:
    virtual void dispose(){
        delete[] ptr;
        ptr = 0;
    }
public:
    reference_base_range(T * ptr) : ptr(ptr){}
    pointer_type ptr;
};

/***********************************************************/

//! base class for shared memory
template <class T, class reference_base_type>
class shared_memory{
    public:
        typedef T element_type;

        //! Standard constructor, points to null
        shared_memory() : reference_counter(new reference_base_type(0)){}

        //! Constructs the shared_memroy and creates a new reference_base
        template<class Y> shared_memory(Y * ptr){
            try{
                reference_counter = new reference_base_type(ptr);
            }catch(std::bad_alloc &e){
                delete ptr;
                throw;
            }
        }
        //! Copies the shared_memory and increases the reference count
        shared_memory(const shared_memory & o) throw() : reference_counter(o.reference_counter){
            o.reference_counter->increase();
        }

        //! Copies the shared_memory of another pointer type and increases the reference count.
        //! Needs the same reference_base_type
        template<class Y> 
        shared_memory(const shared_memory<Y,reference_base_type> & o) throw() : reference_counter(o.reference_counter){
            reference_counter->increase();
        }

        //! Destroys the shared_memory object and deletes the reference_counter if this was the last
        //! reference.        
        ~shared_memory(){
            reference_counter->decrease();
            if(reference_counter->use_count() == 0)
                delete reference_counter;
        }

        //! Returns the number of references
        size_t use_count() const{
            return reference_counter->use_count();
        }

        //! Returns a pointer to the refered memory
        T * get() const{
            return reference_counter->ptr;
        }

        //! Checks whether this object is unique
        bool unique() const{
            return use_count() == 1;
        }        

        //! Checks whehter this object is valid
        operator bool() const{
            return get() != 0;
        }

        //! Checks doesn't reference anythign
        bool empty() const{
            return get() == 0;
        }

        //! Assignment operator for derived classes
        template<class Y> 
        shared_memory& operator=(const shared_memory<Y,reference_base_type> & o){
            shared_memory<Y,reference_base_type> tmp(o);
            swap(tmp);
        }

        //! Assignment operator
        shared_memory& operator=(const shared_memory & o){
            shared_memory tmp(o);
            swap(tmp);
            return *this;
        }

        /** resets the ptr to NULL. If this was the last shared_memory object
        *   owning the referenced object, the object gets deleted.
        *   \sa ~shared_memory
        */
        void reset(){
            shared_memory tmp;
            swap(tmp);
        }

        /** releases the old object and takes a new one
        */
        template <class Y>
        void reset(Y * ptr){
            shared_memory tmp(ptr);
            swap(tmp);
        }        

        /** swaps the owned objects of two shared_memory objects.
        */
        void swap(shared_memory & r){
            reference_base_type * tmp = reference_counter;
            reference_counter = r.reference_counter;
            r.reference_counter = tmp;
        }

    protected:        
        reference_base_type * reference_counter;    //!< Actually reference counter and raw pointer
};

/***********************************************************/

//! ptr (single object) specialization
template <class T>
class shared_ptr : public shared_memory<T,reference_base_ptr<T> >{
    typedef reference_base_ptr<T> reference_counter_type;
    typedef shared_memory<T,reference_counter_type> super;
    typedef T element_type;
public:
    shared_ptr(){}
    template<class Y> shared_ptr(Y * ptr){
        try{
            super::reference_counter = new reference_counter_type(ptr);
        }catch(std::bad_alloc &e){
            //couldn't allocated memory for reference counter
            delete ptr; // prevent memory leak
            throw bad_alloc();
        }
    }
    element_type & operator*() const{
        return *(super::reference_counter->ptr);
    }
    element_type * operator->() const{
        return super::reference_counter->ptr;
    }
};

/***********************************************************/

//! array (range) specialization
template <class T>
class shared_array : public shared_memory<T,reference_base_range<T> >{
    typedef reference_base_range<T> reference_counter_type;
    typedef shared_memory<T,reference_counter_type> super;
    typedef T element_type;

public:
    shared_array(){}
    template<class Y> shared_array(Y * ptr){
        try{
            super::reference_counter = new reference_counter_type(ptr);
        }catch(std::bad_alloc &e){
            delete[] ptr;
            throw bad_alloc();
        }
    }
    element_type & operator[](int i) const{
        return *(super::reference_counter->ptr + i);
    }
};

以下も参照してください。

于 2012-07-26T12:32:00.447 に答える
0

これは の現在の実装ですがboost::shared_ptr<X>、この欠点を共有しない他の実装も考えられます。たとえば、 aを使用して、それぞれへのポインタstatic std::unordered_map<X*, int> std::shared_ptr<X>::share_countの量を追跡できます。ただし、これの欠点は、単純な共有数よりもはるかに大きなオーバーヘッドです。shared_ptr<X>X

于 2012-07-26T13:27:18.243 に答える