Search code examples
c++boostshared-ptr

Why is boost::enable_shared_from_raw so undocumented?


The boost smart_ptr library contains two flavours of enabling a class to provide shared_ptrs to itself, enable_shared_from_this (also available for stl's smart pointers), and enable_shared_from_raw. To me, the latter seems to be superior since it allows shared pointers to be created in the constructor. However, this class is not mentioned in the documentation at all, not part of the root boost/ headers, and googling it mostly gives hits in the actual .hpp file.

Is boost::enable_shared_from_raw deprecated or in some other way unfit for use? Am I missing something?


Solution

  • The problem with enable_shared_from_raw is that it's dangerous; you can inadvertently end up leaking objects.

    If you invoke shared_from_raw without granting ownership of the raw pointer to an instance of shared_ptr, then the enable_shared_from_raw base class will hold a strong reference to itself until you do so.

    As long as it holds a strong reference to itself, the reference count won't be able to reach 0 until the object is manually deleted, which completely removes all benefits of using a shared_ptr. Once something takes ownership of the raw pointer, it demotes it's strong reference to a weak reference and everything is fine.

    class object : boost::enable_shared_from_raw { }
    
    // Leak! When shared_from_raw is called, raw takes ownership of itself.
    object* raw = new object;
    boost::shared_ptr<object> bar = boost::shared_from_raw(raw);
    
    // This is fine; bar already owns the object when shared_from_raw is invoked.
    object* raw = new object;
    boost::shared_ptr<object> bar(raw);
    boost::shared_ptr<object> foo = boost::shared_from_raw(raw);
    
    // Also fine; ownership is transferred to bar.
    object* raw = new object;
    boost::shared_ptr<object> foo = boost::shared_from_raw(raw);
    boost::shared_ptr<object> bar(raw);
    
    // Still a leak! Nothing external has taken ownership of the raw pointer.
    object* raw = new object;
    boost::shared_ptr<object> bar = boost::shared_from_raw(raw);
    boost::shared_ptr<object> foo = bar;
    

    I think the most common case where you would want to invoke shared_from_this() in a constructor is when you want to register the object with some sort of manager; which would most likely result in the leak described here.

    // This use case results in a leak.
    // Nothing external takes ownership of the raw pointer.
    struct Object;
    struct ObjectManager
    {
        void RegisterObject(boost::shared_ptr<Object> obj)
        {
            m_objects.push_back(obj);
        }
        std::list<boost::shared_ptr<Object> > m_objects;
    };
    static ObjectManager gObjectManager;
    
    struct Object : boost::enable_shared_from_raw
    {
        Object() 
        { 
            gObjectManager.RegisterObject(boost::shared_from_raw(this)); 
        }
    }
    

    enable_shared_from_this places additional restrictions to prevent this type of leak.