Search code examples
c++c++11pointersshared-ptr

Convert a shared_ptr to regular a pointer


We have a function that returns a new allocated object as a output argument (ref to pointer).

MyFunc(MyObject*& obj)
{
    obj = new MyObject();
}

Which is called like so:

Object* obj;
MyFunc(obj);

Internally the function does quite a bit and uses shared_ptr for memory management. When it is done, the object we would like to return is referenced by a shared_ptr. I am struggling on how to return our new allocated object as a regular pointer.

We would like to continue to use shared_ptr internally to reduce risks, but it does not seem to make sense to return a shared_ptr as the caller takes complete ownership over the returned value (the called function or object no longer needs or keeps a reference to the returned data) and we want them to have flexibility.

Does anyone have any suggestions for allowing us to use shared_ptr internally but have a regular pointer interface? Thanks


Solution

  • If for some reason you cannot/want not use std::unique_ptr or std::auto_ptr (for example if you need to have multiple owners internally during creation for some reason or your underlying methods require std::shared_ptr to be passed around), you can still make it work with std::shared_ptr by using custom deleter, as described here: https://stackoverflow.com/a/5995770/1274747

    In the principle, after you're done before the return, you switch the deleter to not actually delete the instance (make the deleter "null") and then return by shared_ptr get(). Even after all shared_ptr objects are destroyed, the memory will not be deleted (as the nulled deleter will skip the deletion).

    There is also a link in the comments not so well visible, which might be of your interest: http://paste.ubuntu.com/23866812/ (not sure though if it would really work without the shared ownership of the switch in all cases, would need to test)


    EDIT

    As expected, with the linked simple disarmable deleter from the pastebin you need to be careful, because the deleter is actually copied for storing in std::shared_ptr.

    But you can still make it work by using std::ref:

    MyFunc(MyObject*& obj)
    {
        DisarmableDelete<MyObject> deleter;
        std::shared_ptr<MyObject> ptr(new MyObject(), std::ref(deleter));
        // do what is necessary to setup the object - protected by deleter
        // ...
        // disarm before return
        deleter._armed = false;
        obj = ptr.get();
        // deleter disarmed - object not freed
    }
    

    And just for completeness (and to avoid a potential future broken link), here is the implementation of DisarmableDelete from http://paste.ubuntu.com/23866812/.

    template <typename T, typename Deleter = typename std::default_delete<T> >                                                                                                                              
        struct DisarmableDelete : private Deleter {                                                                                                                                                         
            void operator()(T* ptr) { if(_armed) Deleter::operator()(ptr); }                                                                                                                                
            bool _armed = true;                                                                                                                                                                             
        };