31

Possible Duplicate:
How to release pointer from boost::shared_ptr?

A function of my interface returns a pointer to an object. The user is supposed to take ownership of that object. I do not want to return a Boost.shared_ptr, because I do not want to force clients to use boost. Internally however, I would like to store the pointer in a shared_ptr to prevent memory leaks in case of exceptions etc. There seems to be no way to detach a pointer from a shared pointer. Any ideas here?

Community
  • 1
  • 1
Björn Pollex
  • 75,346
  • 28
  • 201
  • 283

5 Answers5

28

What you're looking for is a release function; shared_ptr doesn't have a release function. Per the Boost manual:

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.

Two options you might consider:

  • You could use std::tr1::shared_ptr, which would require your users to use a C++ library implementation supporting TR1 or to use Boost; at least this would give them the option between the two.
  • You could implement your own boost::shared_ptr-like shared pointer and use that on your external interfaces.

You might also look at the discussion at this question about using boost::shared_ptr in a library's public interface.

Community
  • 1
  • 1
James McNellis
  • 348,265
  • 75
  • 913
  • 977
27

there's always a way :-)

There is indeed a reason why they don't provide a release() method, but it's not impossible to create one. Make your own deleter. Something on the line of (haven't actually compiled the code, but this is the general notion):

template <typename T>
class release_deleter{
public:
  release_deleter() : released_(new some_atomic_bool(false)){}
  void release() {released_->set(true);}
  void operator()(T* ptr){if(!released_->get()) delete ptr;}
private:
  shared_ptr<some_atomic_bool> released_;
}

..

shared_ptr<some_type> ptr(new some_type, release_deleter<some_type>());

..

release_deleter<some_type>* deleter = get_deleter<release_deleter<some_type>>(ptr);
deleter->release();
some_type* released_ptr = ptr.get();
Magnus
  • 643
  • 8
  • 15
  • Why is the private `released_` member a shared_ptr? – Martin Ba Sep 29 '11 at 22:21
  • 1
    I was wondering the same thing. I did it as a regular bool and it suited my needs perfectly. – David Sep 29 '11 at 22:24
  • No reason. I hadn't actually looked into the code, but now I see that deleter is stored with the shared_count, so no additional sharing would be required. – Magnus Oct 07 '11 at 11:04
  • Why `some_atomic_bool` instead of `bool`? Do you aim at some kind of MT safety? – curiousguy Oct 08 '11 at 14:05
  • Depending on how the releaser is used, the release operation may have to be atomic, though the example above isn't entirely up for the task. If the release() function is used from multiple threads, it will have to be atomic, and also indicate to the caller if ownership was obtained or not. A bool return value would do fine. Otherwise, there's a risk for double deletes or leaks. – Magnus Oct 24 '11 at 14:31
  • 5
    Thanks. I ended up with this more no-nonsense version that doesn't hardcode the delete action: http://paste.ubuntu.com/23866812/ – sehe Jan 26 '17 at 01:22
  • @sehe Link is dead. – Chnossos Nov 21 '22 at 15:42
11

The user is supposed to take ownership of that object. I do not want to return a Boost.shared_ptr,

shared_ptr expresses shared ownership, and you want your interface to express transfer of ownership. std::auto_ptr would thus be more applicable here.

Internally however, I would like to store the pointer in a shared_ptr to prevent memory leaks in case of exceptions

Again, shared_ptr may not be the best tool for that job. To prevent leaks in the case of exceptions, scoped_ptr or auto_ptr would be better suited.

Éric Malenfant
  • 13,938
  • 1
  • 40
  • 42
6

Use a shared_ptr to a scoped_ptr to the resource (shared_ptr<scoped_ptr<Resource>>). That way you get shared_ptr's reference counting, which will automatically destroy the resource if and only if it's still attached to the scoped_ptr. But you can detach the scoped_ptr when you're ready to hand off ownership.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Clever, but now you have to change *p to **p every where. This shows a problem with the `shared_ptr` design. – curiousguy Oct 08 '11 at 22:47
  • 2
    @curiousguy: How often do you use `*` on a smart pointer? Usually you use `->`, which has chaining semantics. And anyway, the exact same syntax wasn't required in the question, and this has the advantage of working exactly like the OP wanted. – Ben Voigt Oct 09 '11 at 00:29
  • 1
    "_How often do you use * on a smart pointer?_" I don't know. "_Usually you use ->, which has chaining semantics._" Clever, I forgot that. But I don't like the double indirection: since the lack of `release` is just a shortcoming of `shared_ptr` design, so I wouldn't want to change code that uses it just for this reason. I prefer to work around the arbitrary limitation by changing the deleter: that's an additional indirection too, but it doesn't affect code that uses the smart pointer. (But in reality I would rather write my own smart pointer without `shared_ptr` limitations.) – curiousguy Oct 09 '11 at 00:56
  • Very cool. This works around the problem with binding `std::unique_ptr` to an `std::function`. – Oleg Andriyanov Aug 11 '16 at 16:55
2

As James has well covered you can't really detach a shared pointer.

Do you need multiple owners internally, or are you transferring ownership from your class to the client? In that case a std::auto_ptr might fit the bill.

If you're worried about the surprising semantics of std::auto_ptr, you could hold it internally by boost::scoped_ptr, and detach it at the point you hand it out - leaving it up to the client to manually delete it or store it in their own smart pointer.

If you do have multiple owners on your side you could use an intrusive count. Internally you could then use boost::intrusive__ptr, but hand off the raw pointer at the interface. The client can then either manually work with ref counts, or store it in a boost::intrusive_ptr themselves (but you don't make them depend on it)

philsquared
  • 22,403
  • 12
  • 69
  • 98