4

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

假设我分配了一个类 X 的单个实例,然后将它传递给两个不同的 shared_ptr 实例。据我了解,每个 shared_ptr 实例都不知道另一个实例,因此两个 shared_ptr 实例都引用同一个 X 实例,同时保持 refcount 为 1。如果一个 shared_ptr 超出范围而另一个没有,则X 对象将被删除(因为 refcount 降至零)并且剩余的 shared_ptr 将有一个悬空指针。为了保持 shared_ptr 引用计数,您必须从另一个 shared_ptr 创建一个 shared_ptr。

我对吗 ?如果没有,boost 如何跟踪哪些 shared_ptrs 正在引用一个对通过 shared_ptrs 引用的事实一无所知的类?

4

2 回答 2

7

基本上你是对的。您的示例将导致悬空指针(请注意,如果您boost::enable_shared_from_this用作基类,则会出现一些例外情况)。

解释

问题

boost:shared_ptrstd::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原始指针创建第一个智能指针后,只要不是从. boost 有点等价于这个,但是混合原始指针和智能指针仍然是一个坏主意。更好的是 - 如果您使用智能指针,请不要使用原始指针: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>,但可以想到其他没有这个缺点的实现。例如,astatic std::unordered_map<X*, int> std::shared_ptr<X>::share_count可用于跟踪shared_ptr<X>指向每个 的指针的数量X。但是,这样做的缺点是开销远大于简单的共享计数。

于 2012-07-26T13:27:18.243 回答