How to release pointer from boost::shared_ptr?
Asked Answered
S

13

41

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 :)

Stevana answered 6/10, 2009 at 13:57 Comment(4)
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.Axel
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.Leyden
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!Richma
Related: #28922988Lightness
B
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.

Bullet answered 19/5, 2013 at 11:15 Comment(0)
S
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.

Selves answered 6/10, 2009 at 15:45 Comment(1)
Ideally, release would succeed only if use_count() == 1. That would work. :|Evaleen
C
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() );
Culinary answered 6/10, 2009 at 14:56 Comment(5)
It is interesting, but what would be the purpose of using a shared_ptr in the first place?Furnace
@UncleBens, such deleter has very limited usage. For instance, when you need to call a function and pass to it something like shared_ptr<some_t>( this ). That will be safe only with NullDeleter.Culinary
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.Crappie
Magnus supplies a complete solution here.Bullet
If you can use a null deleter, you probably have no need for smart_ptr.Motorist
M
6

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?

Motorist answered 8/10, 2011 at 22:39 Comment(13)
@Xeo No. See boost: use_count "Returns: the number of shared_ptr objects, *this included, that share ownership with *this," or Visual Studio 2010: use_count "_returns the number of shared_ptr objects that own the resource that is owned by *this. And unique() tests exactly that: "use_count() == 1" This is not what is needed here. Yet another problem with shared_ptr.Motorist
Well, what else do you want from your last question?Katerinekates
@Katerinekates 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).Motorist
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.Katerinekates
@Katerinekates "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_?Motorist
@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.Sharonsharona
+1 for providing an answer to the question, pity there isn't a nicer way.Richma
@Richma Yes, it is a pity, because it would be a useful feature. Complain to Boost, and to the C++ committee!Motorist
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.Pelican
Aside of everything, I think constructing a new object in-place without first calling a non-trivial destructor is unspecified, if not undefined behaviorPelite
@Pelite 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).Motorist
And it even goes on saying "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."Motorist
This answer cannot be a solution. As author states there is a memory leak. valgrind proves, there is really a memory leak. If one is agree to have a memory leak, more safe way is to new std::shared_ptr{std::move(x)};` (just to leak the shared ptr itself)Dib
B
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.

Bullet answered 19/5, 2013 at 11:15 Comment(0)
R
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.

Rodneyrodolfo answered 6/10, 2009 at 14:28 Comment(0)
B
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.

Blenny answered 11/9, 2012 at 14:57 Comment(0)
I
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;
}
Invaluable answered 4/12, 2012 at 11:23 Comment(0)
S
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.

Somewhat answered 6/10, 2009 at 14:24 Comment(2)
@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.Somewhat
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).Motorist
C
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.

Crackle answered 4/8, 2017 at 15:21 Comment(0)
C
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();
}
Cabinda answered 29/9, 2011 at 22:31 Comment(4)
@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.Cabinda
"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.Motorist
@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.Cabinda
"to leave a smart_ptr reference hanging around without ever being destroyed" I didn't knew that static variables are never destroyed.Motorist
B
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.

Blocky answered 19/5, 2013 at 10:34 Comment(0)
M
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.

Magee answered 28/3, 2017 at 0:50 Comment(0)
M
-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 *

Meloniemelony answered 7/3, 2016 at 10:30 Comment(4)
This causes a permanent, unrecoverable memory leak. It's hard to see a case where that would be acceptable.Anapest
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 allMeloniemelony
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.Anapest
In my situation, it's done on load up. All other methods would leak in the same wayMeloniemelony

© 2022 - 2024 — McMap. All rights reserved.