Detach a Pointer from a Shared_Ptr

Detach a pointer from a shared_ptr?

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.

Does a detached thread keep its captured shared_ptr alive?

Does a detached thread keep its captured shared_ptr alive?

Yes. The lambda - and thus also the captures of the lambda - will stay alive as long as the thread is executing. Same would also apply to args passed into std::thread.

we're deleting our own handle to the std::thread at the end of the scope

The std::thread object won't contain the functor. It will need to be stored in dynamic memory. The lifetime of the executing thread is separate from the lifetime of the std::thread wrapper.

Delete shared pointer by memory address from vector

Yes it's possible to remove a pointer from a container of pointers by address.

While std::vector<std::shared_ptr<T>> is a normal way of storing shared pointers, the question of which way is better can not be answered without knowing more about your requirements.

Here is an expansion of your template to demonstrate pointer removal by address, using your own removePerson() function:

#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

// A minimal Person type for demonstration purposes
struct Person {
Person(const std::string& n): name(n) {}
std::string name;
};

// Original template
std::vector<std::shared_ptr<Person>> persons;

void removePerson(const std::shared_ptr<Person>& person) {
auto remove = std::remove(persons.begin(), persons.end(), person);
persons.erase(remove, persons.end());
}

int main()
{
auto bob = std::make_shared<Person>("bob");

// Include bob multiple times to demonstrate multiple removal
persons.emplace_back(bob);
persons.emplace_back(std::make_shared<Person>("alice"));
persons.emplace_back(bob);
persons.emplace_back(std::make_shared<Person>("carol"));
persons.emplace_back(bob);

// Define a lambda for debugging output
auto printPersons = [&] (const auto& header) {
std::cout << header << std::endl;
for(auto& p: persons)
std::cout << p->name << std::endl;
};

printPersons("== Before ==");
removePerson(bob);
printPersons("\n== After ==");
}

This yields the following output:

== Before ==
bob
alice
bob
carol
bob

== After ==
alice
carol

Note: A std::vector would usually not be named list because it's, well, not a list ;-)

Convert a shared_ptr to regular a pointer

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;
};

Delete std::shared_ptr without destroying the managed object?

static std::map m<data *, std::shared_ptr<data> >;

struct container {
data* ptr;
};

void someFunc(container* receiver /* wants to be filled */) {
auto myData = createData(); // returns shared_ptr<data>, I can't do anything about it
receiver->ptr = myData.get();
m[receiver->ptr] = myData;
}

void someOtherFunc(container* receiver)
{
// delete receiver->ptr;
m.erase(receiver->ptr);
}

This elongates the life of shared_ptr through a map.

How to release pointer from boost::shared_ptr?

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.

Why would I std::move an std::shared_ptr?

I think that the one thing the other answers did not emphasize enough is the point of speed.

std::shared_ptr reference count is atomic. increasing or decreasing the reference count requires atomic increment or decrement. This is hundred times slower than non-atomic increment/decrement, not to mention that if we increment and decrement the same counter we wind up with the exact number, wasting a ton of time and resources in the process.

By moving the shared_ptr instead of copying it, we "steal" the atomic reference count and we nullify the other shared_ptr. "stealing" the reference count is not atomic, and it is hundred times faster than copying the shared_ptr (and causing atomic reference increment or decrement).

Do note that this technique is used purely for optimization. copying it (as you suggested) is just as fine functionality-wise.

Giving a std::shared_ptrstd::thread to itself. Defined or Undefined behavior

std::thread::detach releases ownership - when the shared_ptr is destroyed nothing will happen to the detached thread of execution.

This is bad practice because you could simply write...

std::thread{[]{ std::cout<< "HelloWorld" << std::endl; }}.detach();

...to spawn a background thread that cleans up after itself.

Disable the cleaning of std::shared_ptr

If you were to replace the new with std::unique_ptr instead, you could use std::unique_ptr::release to get ownership over a raw pointer (so you can return it):

T* oldfn() {
T* ptr = new T(args...);
// use `ptr`
delete ptr;
T* ptr2 = new T(args...);
// use `ptr2`
return ptr2;
}

// Becomes

T* newfn() {
std::unique_ptr<T> ptr{ new T(args...) };
// use `ptr.get()`
// RAII will delete
std::unique_ptr<T> ptr2{ new T(args...) };
// use `ptr2.get()`
return ptr2.release();
}

You cannot do this easily with a shared pointer because the shared pointer itself doesn't own the pointer on it's own, since you can make copies of the shared pointer that point to the same resource, so releasing ownership through one pointer is hard.



Related Topics



Leave a reply



Submit