40

Can boost::shared_ptr release the stored pointer without deleting it?

I can see no release function exists in the documentation, also in the FAQ is explained why it does not provide release function, something like that the release can not be done on pointers that are not unique. My pointers are unique. How can I release my pointers ? Or which boost smart pointer class to use that will allow me releasing of the pointer ? I hope that you won't say use auto_ptr :)

ks1322
  • 33,961
  • 14
  • 109
  • 164
user152508
  • 3,053
  • 8
  • 38
  • 58
  • 1
    Why not `auto_ptr`? If they're unique, it must mean they never get copied around (as multiple references would then exist, if only temporarily), and then `auto_ptr` should work just fine. Or, if you don't plan on using the smart pointer-supplied lifetime management *anyway*, use a raw pointer. – jalf Nov 05 '09 at 14:48
  • 1
    In addition to reference count semantics, shared_ptr also provides a custom deleter facility that auto_ptr does not. So here's a scenario: you create an object using a custom allocator (i.e. not global new/delete), and you want a smart pointer for exception safety while you configure the object, but you need to return a raw pointer once you're done doing things that might throw. Unfortunately, neither auto_ptr nor any of the boost smart_ptrs seem to support this. – Trevor Robinson Aug 20 '10 at 20:38
  • 2
    Had this problem with 3rd party interface. Some interfaces return a 'unique' `shared_ptr` from factories, since there is a case for that being the best way pre-C++11. A throwing `shared_ptr` -> `unique_ptr` conversion might be useful, it's a pain when you can't break rules even when you really want to! – Zero Nov 26 '12 at 01:28
  • Related: http://stackoverflow.com/questions/28922488/delete-stdshared-ptr-without-destroying-the-managed-object/28922647#28922647 – Mohit Jain Mar 08 '15 at 03:14

14 Answers14

31

Don't. Boost's FAQ entry:

Q. Why doesn't shared_ptr provide a release() function?

A. shared_ptr cannot give away ownership unless it's unique() because the other copy will still destroy the object.

Consider:

shared_ptr<int> a(new int);
shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2

int * p = a.release();

// Who owns p now? b will still call delete on it in its destructor.

Furthermore, the pointer returned by release() would be difficult to deallocate reliably, as the source shared_ptr could have been created with a custom deleter.

So, this would be safe in case it's the only shared_ptr instance pointing to your object (when unique() returns true) and the object doesn't require a special deleter. I'd still question your design, if you used such a .release() function.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
24

You could use fake deleter. Then pointers will not be deleted actually.

struct NullDeleter {template<typename T> void operator()(T*) {} };

// pp of type some_t defined somewhere
boost::shared_ptr<some_t> x(pp, NullDeleter() );
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • 7
    It is interesting, but what would be the purpose of using a shared_ptr in the first place? – UncleBens Oct 06 '09 at 15:48
  • 2
    @UncleBens, such deleter has very limited usage. For instance, when you need to call a function and pass to it something like `shared_ptr( this )`. That will be safe only with `NullDeleter`. – Kirill V. Lyadvinsky Oct 06 '09 at 16:54
  • 1
    Yup, we used something like this when the interface (which we didn't control) required a shared_ptr and guaranteed that it wouldn't store the pointer past the function call. – McBeth Nov 05 '09 at 14:43
  • [Magnus supplies a complete solution here.](http://stackoverflow.com/questions/1833356/detach-a-pointer-from-a-shared-ptr/5995770#5995770) – Martin Ba Sep 29 '11 at 22:18
  • 1
    If you can use a null deleter, you probably have no need for `smart_ptr`. – curiousguy Oct 08 '11 at 22:43
7

Kids, don't do this at home:

// set smarty to point to nothing
// returns old(smarty.get())
// caller is responsible for the returned pointer (careful)
template <typename T>
T* release (shared_ptr<T>& smarty) {
    // sanity check:
    assert (smarty.unique());
    // only one owner (please don't play games with weak_ptr in another thread)
    // would want to check the total count (shared+weak) here

    // save the pointer:
    T *raw = &*smarty;
    // at this point smarty owns raw, can't return it

    try {
        // an exception here would be quite unpleasant

        // now smash smarty:
        new (&smarty) shared_ptr<T> ();
        // REALLY: don't do it!
        // the behaviour is not defined!
        // in practice: at least a memory leak!
    } catch (...) {
        // there is no shared_ptr<T> in smarty zombie now
        // can't fix it at this point:
        // the only fix would be to retry, and it would probably throw again
        // sorry, can't do anything
        abort ();
    }
    // smarty is a fresh shared_ptr<T> that doesn't own raw

    // at this point, nobody owns raw, can return it
    return raw;
}

Now, is there a way to check if total count of owners for the ref count is > 1?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • @Xeo No. See boost: [use_count](http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/shared_ptr.htm#use_count) "_Returns: the number of `shared_ptr` objects, *this included, that share ownership with *this,_" or Visual Studio 2010: [use_count](http://msdn.microsoft.com/fr-fr/library/bb982325.aspx) "_returns the number of shared_ptr objects that own the resource that is owned by *this. And `unique()` [tests exactly that](http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/shared_ptr.htm#unique): "`use_count() == 1`" This is not what is needed here. Yet another problem with `shared_ptr`. – curiousguy Oct 08 '11 at 23:03
  • Well, what else do you want from your last question? – Xeo Oct 08 '11 at 23:04
  • @Xeo As I wrote: "would want to check the total count (shared+weak) here". `use_count` and `unique` only consider `shared_ptr`. I need all shared owners of the internal `shared_ptr` data structure, and that includes the number of `weak_ptr` as well. I just need in Boost: `pn.pi_->use_count_ + pn.pi_->weak_count_ - 1` (no need to be atomic). – curiousguy Oct 08 '11 at 23:29
  • Ah, I see now. Though, I don't think there is a way to get the weak count. :/ Also, the weak pointer don't "own" the ref count or the pointer, hence the misunderstanding. – Xeo Oct 09 '11 at 08:58
  • @Xeo "_the weak pointer don't "own" the ref count or the pointer_" Then what does it do with it? What is `weak_ptr` if it isn't a ref counted shared pointer to `pi_`? – curiousguy Oct 09 '11 at 10:00
  • @curiousguy: It is like a "weak reference" on an object which memory management is assumed by a shared_ptr. It does not increase nor decrease the ref count and throws exceptions if the object was deleted when you try to use it via the weak_ptr. – BlueTrin Oct 05 '12 at 08:10
  • 2
    +1 for providing an answer to the question, pity there isn't a nicer way. – Zero Nov 26 '12 at 01:29
  • @Zero Yes, it is a pity, because it would be a useful feature. Complain to Boost, and to the C++ committee! – curiousguy Jan 23 '13 at 18:55
  • 1
    Let me point out two serious problems with this solution: 1> if you used `make_shared` then `release` will not return a pointer you can use with `delete` because of the way `make_shared` allocates memory. 2> `weak_ptr` usually uses the subscription pattern so if you don't run the `shared_ptr` destructor the `weak_ptr`'s `lock()` method will return you a valid `shared_ptr` that owns the memory again. It probably won't delete it though as the reference count is now off by one. – Fozi Aug 20 '14 at 17:18
  • Aside of everything, I think constructing a new object in-place without first calling a non-trivial destructor is unspecified, if not undefined behavior – sehe Mar 31 '18 at 10:49
  • @sehe The effect of reusing the storage of an object is described in **[basic.life]** "The lifetime of an object o of type T ends when: (...) the storage which the object occupies (...) is reused by an object that is not nested within o ([intro.object])." (recent draft) Older std text is the same except of that "nest" invention (lol). – curiousguy Jun 26 '18 at 22:02
  • 1
    And it even goes on [saying](http://eel.is/c++draft/basic.life#5) "For an object of a class type with a non-trivial destructor, **the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released**; however, if there is no explicit call to the destructor or if a delete-expression is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior." – curiousguy Jun 26 '18 at 22:03
6

You need to use a deleter that you can request not to delete the underlying pointer.

See this answer (which has been marked as a duplicate of this question) for more information.

Community
  • 1
  • 1
Martin Ba
  • 37,187
  • 33
  • 183
  • 337
4

To let the pointer point to nothing again, you can call shared_ptr::reset().

However, this will delete the object pointed to when your pointer is the last reference to the object. This, however, is exactly the desired behaviour of the smart pointer in the first place.

If you just want a reference that does not hold the object alive, you can create a boost::weak_ptr (see boost documentation). A weak_ptr holds a reference to the object but does not add to the reference count, so the object gets deleted when only weak references exist.

Christopher
  • 8,912
  • 3
  • 33
  • 38
Timbo
  • 27,472
  • 11
  • 50
  • 75
3

The basis of sharing is trust. If some instance in your program needs to release the raw pointer, it is almost for sure that shared_ptr is the wrong type.

However, recently I wanted to do this too, as I needed to deallocate from a different process-heap. In the end I was taught that my older decision to use some std::shared_ptr was not thought-out.

I just routinely used this type for cleanup. But the pointer was just duplicated on a few places. Actually I needed a std::unique_ptr, which (suprise) has a release function.

Andreas Spindler
  • 7,568
  • 4
  • 43
  • 34
2

Forgive them for they know not what they do. This example works with boost::shared_ptr and msvs std::shared_ptr without memory leaks!

template <template <typename> class TSharedPtr, typename Type>
Type * release_shared(TSharedPtr<Type> & ptr)
{
    //! this struct mimics the data of std:shared_ptr ( or boost::shared_ptr )
    struct SharedVoidPtr
    {
        struct RefCounter
        {
            long _Uses;
            long _Weaks;
        };

        void * ptr;
        RefCounter * refC;

        SharedVoidPtr()
        {
            ptr = refC = nullptr;
        }

        ~SharedVoidPtr()
        {
            delete refC;
        }
    };

    assert( ptr.unique() );

    Type * t = ptr.get();

    SharedVoidPtr sp; // create dummy shared_ptr
    TSharedPtr<Type> * spPtr = (TSharedPtr<Type>*)( &sp );
    spPtr->swap(ptr); // swap the contents

    ptr.reset();
    // now the xxx::shared_ptr is empy and
    // SharedVoidPtr releases the raw poiter but deletes the underlying counter data
    return t;
}
1

You can delete the shared pointer, which seems much the same to me. If pointers are always unique, then std::auto_ptr<> is a good choice. Bear in mind that unique pointers can't be used in STL containers, since operations on them do a lot of copying and temporary duplication.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
  • @curiousguy: In which case I'm a little fuzzy on the semantics of "release", not to mention way fuzzy on what the OP really wants to do. – David Thornley Oct 08 '11 at 20:49
  • Simple: he has converted a pointer to a `share_ptr` (constructed a `share_ptr` with some argument `p`), he wants to do the reverse operation (revert the construction of `share_ptr`). – curiousguy Oct 08 '11 at 22:08
1

I'm not entirely sure if your question is about achieving this, but if you want behaviour from a shared_ptr, where, if you release the value from one shared_ptr, all the other shared pointers to the same value become a nullptr, then you can put a unique_ptr in a shared_ptr to achieve that behaviour.

void print(std::string name, std::shared_ptr<std::unique_ptr<int>>& ptr)
{
    if(ptr == nullptr || *ptr == nullptr)
    {
        std::cout << name << " points to nullptr" << std::endl;
    }
    else
    {
        std::cout << name << " points to value " << *(*ptr) << std::endl;
    }
}

int main()
{
    std::shared_ptr<std::unique_ptr<int>> original;
    original = std::make_shared<std::unique_ptr<int>>(std::make_unique<int>(50));

    std::shared_ptr<std::unique_ptr<int>> shared_original = original;

    std::shared_ptr<std::unique_ptr<int>> thief = nullptr;

    print(std::string("original"), original);
    print(std::string("shared_original"), shared_original);
    print(std::string("thief"), thief);

    thief = std::make_shared<std::unique_ptr<int>>(original->release());

    print(std::string("original"), original);
    print(std::string("shared_original"), shared_original);
    print(std::string("thief"), thief);

    return 0;
}

Output:

original points to value 50
shared_original points to value 50
thief points to nullptr
original points to nullptr
shared_original points to nullptr
thief points to value 50

This behaviour allows you to share a resource (like an array), then later reuse that resource while invalidating all the shared references to this resource.

Aberrant
  • 3,423
  • 1
  • 27
  • 38
0

If your pointers are indeed unique do use std::unique_ptr or boost::scoped_ptr if the former is not available for your compiler. Otherwise consider combining the use of boost::shared_ptr with boost::weak_ptr. Check out the Boost documentation for details.

Nicola Musatti
  • 17,834
  • 2
  • 46
  • 55
0

I am using Poco::HTTPRequestHandlerFactory which expects to return a raw HTTPRequestHandler*, the Poco framework deletes the handler once the request finishes.

Also using DI Sauce project to create the controllers, however the Injector returns shared_ptr which I cannot return directly, and returning handler.get() is no good either since the as soon as this function returns the shared_ptr goes out of scope and deletes then handler before its executed, so here is a reasonable (I think) reason to have a .release() method. I ended up creating a HTTPRequestHandlerWrapper class as follows :-

class HTTPRequestHandlerWrapper : public HTTPRequestHandler {
private:
    sauce::shared_ptr<HTTPRequestHandler> _handler;

public:
    HTTPRequestHandlerWrapper(sauce::shared_ptr<HTTPRequestHandler> handler) {
        _handler = handler;
    }

    virtual void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) {
        return _handler->handleRequest(request, response);
    }
};

and then the factory would

HTTPRequestHandler* HttpHandlerFactory::createRequestHandler(const HTTPServerRequest& request) {
    URI uri = URI(request.getURI());
    auto path = uri.getPath();
    auto method = request.getMethod();

    sauce::shared_ptr<HTTPRequestHandler> handler = _injector->get<HTTPRequestHandler>(method + ":" + path);

    return new HTTPRequestHandlerWrapper(handler);
}

which satisfied both Sauce and Poco and works nicely.

steve
  • 3,230
  • 1
  • 19
  • 14
0

I needed to pass a pointer through async handlers and to keep the self-destruct behavior in case of failure but the final API expected a raw pointer, so I made this function to release from a single shared_ptr:

#include <memory>

template<typename T>
T * release(std::shared_ptr<T> & ptr)
{
    struct { void operator()(T *) {} } NoDelete;
    
    T * t = nullptr;
    if (ptr.use_count() == 1)
    {
        t = ptr.get();
        ptr.template reset<T>(nullptr, NoDelete);
    }
    return t;
}

If ptr.use_count() != 1 you shall get a nullptr instead.


Do note that, from cppreference (bold emphasis mine):

If use_count returns 1, there are no other owners. (The deprecated member function unique() is provided for this use case.) In multithreaded environment, this does not imply that the object is safe to modify because accesses to the managed object by former shared owners may not have completed, and because new shared owners may be introduced concurrently, such as by std::weak_ptr::lock.

Chnossos
  • 9,971
  • 4
  • 28
  • 40
0

Here's a hack that might work. I wouldn't recommend it unless you're in a real bind.

template<typename T>
T * release_shared(std::shared_ptr<T> & shared)
{
    static std::vector<std::shared_ptr<T> > graveyard;
    graveyard.push_back(shared);
    shared.reset();
    return graveyard.back().get();
}
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • @curiousguy, I'm not sure what you mean. Here's the definition of `auto_ptr::release` from cplusplus.com: `"Sets the auto_ptr internal pointer to null pointer (which indicates it points to no object) without destructing the object currently pointed by the auto_ptr"`. This function does that; the `reset` sets the pointer to null, and copying the pointer to a static graveyard object prevents shared_ptr from deleting it. – Mark Ransom Oct 09 '11 at 01:51
  • "_This function does that_" but not only that. The contract of `release` is to do only what is specified. "_copying the pointer to a static graveyard_" is not part of `release` specification. – curiousguy Oct 09 '11 at 02:07
  • @curiousguy, there's no documented way to prevent a shared_ptr from deleting the object it's pointing to. My "hack" as I fully admitted is to leave a smart_ptr reference hanging around without ever being destroyed. This is an implementation detail that is not visible outside of the `release_shared` function. – Mark Ransom Oct 09 '11 at 02:24
  • "_to leave a smart_ptr reference hanging around without ever being destroyed_" I didn't knew that `static` variables are never destroyed. – curiousguy Oct 09 '11 at 02:51
-1

Easy solution, increase the reference and then leak the shared_pointer.

boost::shared_ptr<MyType> shared_pointer_to_instance(new MyType());
new boost::shared_ptr<MyType>();
MyType * raw_pointer = shared_pointer_to_instance.get()

This will clearly cause a memory leak of both the shared_ptr and the MyType *

robby987
  • 827
  • 1
  • 9
  • 25
  • This causes a permanent, unrecoverable memory leak. It's hard to see a case where that would be acceptable. – David Schwartz Jun 20 '16 at 09:31
  • Yes, that's the point. The entire point of the exercise it to prevent the shared pointer from deleting the object. The only thing that is leaked is the shared pointer, which will only be a few bytes and will have no significance on the program at all – robby987 Jun 22 '16 at 01:50
  • 1
    If you do it once it will have no significance. But we often write programs to do things we want to do over and over. – David Schwartz Jun 23 '16 at 13:10
  • In my situation, it's done on load up. All other methods would leak in the same way – robby987 Jun 25 '16 at 12:13