The following program crashes with a bad glibc double free error:
#include <iostream>
#include <memory>
class foo {
public:
foo()
{
std::cout << "foo constructed" << std::endl;
}
~foo()
{
std::cout << "foo destructed" << std::endl;
}
};
int main() {
auto f = std::make_shared< foo >();
std::cout << "Before reset" << std::endl;
f.reset( f.get() );
std::cout << "After reset" << std::endl;
return 0;
}
From this I get the following output (followed by the glibc error):
foo constructed
Before reset
foo destructed
After reset
foo destructed
So obviously in this case the object is destroyed twice. Once by the reset and once by the std::shared_ptr
going out of scope. This is actually what I would have expected.
On cppreference however I find the following text (found at http://en.cppreference.com/w/cpp/memory/shared_ptr/reset ):
If *this already owns an object and it is the last shared_ptr owning it, the object is destroyed through the owned deleter, unless ptr is a pointer to it.
In my opinion this actually says, that the object should not be destroyed as in my example. Quite surprising, but if the standard says so. Am I somehow misreading this, or is the implementation of std::shared_ptr
I have available not conforming to the standard?
For those who ask why I am doing this:
I am currently trying to figure out how to temporarily manage bare pointers to objects created by new
and new[]
. The idea is to use the std::shared_ptr::reset()
to replace the deleter with a no-op deleter. The alternative is to wrap the code with a try { stuff() } catch( ... ) { delete x; throw;}
kind of block.
The specification for that overload of reset
is given in 20.7.2.2.4 shared_ptr modifiers [util.smartptr.shared.mod], paragraph 3 (from n3290):
template<class Y> void reset(Y* p);
Effects: Equivalent to
shared_ptr(p).swap(*this)
.
As you can see, that shared_ptr(p)
construction creates a new count with new deleter for that p
, so nothing good can come off it. It really is the best to think of std::shared_ptr<T>::get
as strictly an observer, and using it to deal with lifetime management is a telltale sign that there is something wrong going on.
On the other hand, std::unique_ptr<T>
has release
, which is exactly what you need when you need to step in and deal with ownership yourself for an instant. Perhaps you can change your design to use std::unique_ptr<T>
? It's always possible to create a std::shared_ptr<T>
out of it if you need to, eventually. (Although while the std::shared_ptr<T>
picks the deleter from the std::unique_ptr<T>
, you still need special treatment in the array case as you probably want std::shared_ptr<T*>
from std::unique_ptr<T[]>
.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With