boost shared_ptr的基础设计

Ste*_*ini 4 c++ boost

我试图理解boost shared_ptr类的底层设计.我想把它"移植"到fortran(不要问).我理解的一件事是引用计数由shared_count类保存.这提示我一个问题.我很久没有使用过C++,也从未使用过boost.

假设我分配了一个类X的单个实例,然后将其传递给两个不同的shared_ptr实例.据我了解,每shared_ptr的实例不知道其他任何东西,因此,这两shared_ptr实例指的是相同的X实例,同时保持1引用计数,如果一个shared_ptr离开的范围,而其他没有, X对象将被删除(因为refcount降为零),剩下的shared_ptr将有一个悬空指针.为了保持shared_ptr引用计数,您必须从另一个shared_ptr创建shared_ptr.

我对吗 ?如果没有,那么如何提升跟踪哪些shared_ptrs引用了一个对通过shared_ptrs引用的事实一无所知的类?

Zet*_*eta 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
Run Code Online (Sandbox Code Playgroud)

"解"

在S从指向p对象的原始指针创建第一个智能指针后,O只应使用复制构造函数S,而不是使用复制构造函数p,只要O它不是派生的std::enable_shared_from_this.boost有点相当于此,但混合原始指针和智能指针仍然是一个坏主意.更好 - 如果使用智能指针,请不要使用原始指针:

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
Run Code Online (Sandbox Code Playgroud)

更好的是,不要自己分配内存,而是使用std::make_shared或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
Run Code Online (Sandbox Code Playgroud)

可能的实施

以下实现std::shared_ptr与之相比非常粗糙,因为它不支持std::weak_ptr和std::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);
    }
};
Run Code Online (Sandbox Code Playgroud)

也可以看看: