Consider the following code:
#include <iostream>
#include <memory>
using namespace std;
class T;
std::weak_ptr<T> wptr;
class T
{
public:
T() { }
~T() {
std::cout << "in dtor" << std::endl;
std::cout << (wptr.expired() ? "expired" : "not expired") << std::endl;
}
};
int main() {
{
auto ptr = std::make_shared<T>();
wptr = ptr;
std::cout << (wptr.expired() ? "expired" : "not expired") << std::endl;
}
return 0;
}
In this code, I was trying to find out if weak_ptrs are expired in the objects destruction phase. It seems so. The output is:
not expired
in dtor
expired
I used gcc-5.1 with ideone.
Now, I have another problem. I couldn't find any documentation stating that this is the standard behavior. Is it guaranteed to work this way, always?
Now, I have another problem. I couldn't find any documentation stating that this is the standard behavior. Is it guaranteed to work this way, always?
No. Indeed, it's underspecified in the standard, as raised by LWG issue 2751.
The C++14 standard contains no language that guarantees the deleter run by a shared_ptr will see all associated weak_ptr instances as expired. For example, the standard doesn't appear to guarantee that the assertion in the following snippet won't fire:
std::weak_ptr<Foo> weak;
std::shared_ptr<Foo> strong{
new Foo,
[&weak] (Foo* f) {
assert(weak.expired());
delete f;
},
};
weak = strong;
strong.reset();
It seems clear that the intent is that associated weak_ptrs are expired, because otherwise shared_ptr deleters could resurrect a reference to an object that is being deleted.
Suggested fix: 23.11.3.2 [util.smartptr.shared.dest] should specify that the decrease in use_count() caused by the destructor is sequenced before the call to the deleter or the call to delete p.
The current wording for ~shared_ptr() as linked above simply states that the deleter is invoked, with a non-normative note that the number of instances that share ownership is decreased.
While the intent is probably that weak.expired() when the deleter is called, it's questionable to rely on this. It's really only reasonable to state with confidence that the shared_ptr no longer shares ownership after it's been destroyed - asking that question during destruction is a bit odd.
Using make_shared like that will create an object with the default constructor you provided.
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
Constructs an object of type T and wraps it in a std::shared_ptr
using args as the parameter list for the constructor of T. The object
is constructed as if by the expression (std::make_shared)
After the anonymous scope in the main. The shared ptr will be deleted.
The object is destroyed and its memory deallocated when either of the
following happens:
the last remaining shared_ptr owning the object is destroyed;
(std::shared_ptr)
.
The destructor of shared_ptr decrements the number of shared owners of
the control block. If that counter reaches zero, the control block
calls the destructor of the managed object. The control block does not
deallocate itself until the std::weak_ptr counter reaches zero as
well. std::shared_ptr Implementation notes
This means that your object will call its destructor after the destruction of the last shared ptr has started.
The output:
not expired
in dtor
expired
is the expected behavior.
Not the standard itself but:
http://en.cppreference.com/w/cpp/memory/weak_ptr/expired
Checks whether the managed object has already been deleted. Equivalent
to use_count() == 0.
So it becomes a question of weather use_count is set to 0 before or after deletion. Now there's a not on this in a draft of the standard:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf [page 566 20.9.2.2.2]
~shared_ptr();
Effects:
If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
Otherwise, if *this owns an object p and a deleter d, d(p) is called.
Otherwise, *this owns a pointer p, and delete p is called.
[Note: Since the destruction of *this decreases the number of
instances that share ownership with *this by one, after *this has
been destroyed all shared_ptr instances that shared ownership with
*this will report a use_count()that is one less than its previous
value. — end note]
A weak_ptr expires when there are no more shared_ptrs referring to the object.
When (immediately after) the last shared_ptr stops referring to the object it's destroyed.
At this point there are no shared_ptrs referring to it, so any weak_ptr has expired.
The object's destructor is now invoked and if it has separate storage (i.e. wasn't created with make_shared) its storage is deallocated.
The control block, where the reference count and raw pointer and delete function are stored, persists if there are any weak_ptrs referring to it. When the last weak_ptr stops referring to it, also the control block is destroyed and deallocated. I.e., shared_ptr instances keep the object itself alive, along with its control block, while weak_ptr instances keep the control block alive.
Related
I have a std::shared_ptr with a custom deleter and in that deleter, I would like to take a temporary copy of the original std::shared_ptr. Expressed in code form:
struct Foo : public std::enable_shared_from_this<Foo>
{};
void deleter(Foo *f)
{
{
std::shared_ptr<Foo> tmp = f->shared_from_this(); // Line A
}
delete f;
}
int main()
{
std::shared_ptr<Foo> foo(new Foo, &deleter);
}
My question is: on line A, can anything be said about the call of shared_from_this()? Is it legal? If so, does the standard say anything about its return value? If we replace enable_shared_from_this with a different weak_ptr or global reference to foo, will the answer be the same?
Clang with libc++ and gcc with libstdc++ both produce code which terminates on a bad_weak_ptr exception, but I can't seem to trace this as required by the standard. Is this implementation-specific, or am I missing a rule?
All the relevant rules I found (quoting C++11):
20.7.2.2.2 shared_ptr destructor
1 ... if *this owns an object p and a deleter d, d(p) is called
2 [Note: ... Since the destruction of *this decreases the number of instances that share ownership with *this
by one, after *this has been destroyed all shared_ptr instances that shared ownership with *this will
report a use_count() that is one less than its previous value. —end note]
20.7.2.2.5 shared_ptr observers
7 use_count Returns: the number of shared_ptr objects, *this included, that share ownership with *this, or 0
when *this is empty.
To me, it seems it's not clear whether the decrement of use_count happens before or after the call of the deleter. Is getting bad_weak_ptr a reliable result, or is this simply unspecified?
Note that I am intentionally avoiding a situation where a pointer like tmp in my example code would outlive the deleter execution.
consider
[c++14-12.4-15]Once a destructor is invoked for an object, the object no longer exists;
and
[c++14-20.8.2.4-7]shared_from_this()[...]Requires: enable_shared_from_this shall be an accessible base class of T. *this shall be a subobject of an object t of type T. There shall be at least one shared_ptr instance p that owns &t.
so, considering that the deleter is invoked by the shared_ptr destructor, calling shared_from_this() in the deleter of the last shared_ptr owning it results in undefined behaviour.
EDIT: as pointed out by YSC, in C++17 shared_from_this() is required to behave as the corresponding weak_ptr conversion call. This complicates matters though, because it's not clear what weak_ptr::expired() should return at deleter call ... anyway, taking the quoted 20.7.2.2.2 note literally, a bad_weak_ptr should be raised in this case.
std::shared_ptr has an aliasing constructor that allows newly created shared_ptr to share state with an existing shared pointer while pointing to some other object.
I was thinking about abusing this constructor to put pointer to some global object inside shared_ptr:
int global = 0;
int main()
{
// because we point to global object we do not need to track its lifetime
// so we use empty shared_ptr<void> as a provider of shared state
std::shared_ptr<int> p(std::shared_ptr<void>(), &global);
std::shared_ptr<int> pp = p;
return *pp;
}
My question is: Is it legal? The code successfully works on major compilers.
Note, that I do not ask if it's a good thing to do. I do understand that there's a canonical way of putting pointers to global objects into shared_ptr using no-op deleter. It is also a bit disturbing if it is legal, because it would be possible to have dereferenceable shared_ptr, weak pointers to which are always expired:
std::shared_ptr<int> p(std::shared_ptr<void>(), &global);
std::weak_ptr<int> w = p;
if (p) // p is alive and well
{ // and w is not
*w.lock(); // and here program crashes
}
As you already know, with your current solution, p has a use_count() of zero, that's why the weak_ptr is expired. This seems to be ok, according to the C++ draft N4296:
20.8.2.2.1 shared_ptr constructors [util.smartptr.shared.const]
template shared_ptr(const shared_ptr& r, T* p) noexcept;
13 Effects: Constructs a shared_ptr instance that stores p and shares ownership with r.
14 Postconditions: get() == p && use_count() == r.use_count()
15 [ Note: To avoid the possibility of a dangling pointer, the user of this constructor must ensure that p
remains valid at least until the ownership group of r is destroyed. — end note ]
16 [ Note: This constructor allows creation of an empty shared_ptr instance with a non-null stored
pointer. — end note ]
20.8.2.2.2 shared_ptr destructor [util.smartptr.shared.dest]
~shared_ptr();
1 Effects:
(1.1) — If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1),
there are no side effects.
(1.2) — Otherwise, if *this owns an object p and a deleter d, d(p) is called.
(1.3) — Otherwise, *this owns a pointer p, and delete p is called
emphasis mine.
You could use the following instead which gives a shared_ptr with a use_count() of one:
std::shared_ptr<int> p(&global, [](int*){});
This uses an empty custom deleter.
In my code, I use a shared pointer using boost's shared_from_this feature. To keep it short, the shared pointer is shared on similar lines as below:
class Q: public enable_shared_from_this<Q>
{
public:
shared_ptr<Q> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Q> p(new Q);
shared_ptr<Q> q = p->f();
.....
.....
}
When I examine this in gdb:
(gdb) p *this
$8 = {
....
<boost::enable_shared_from_this<Q>> = {
weak_this_ = boost::weak_ptr<Q>(refs=0, weak=2) = {
px = (Q *) 0xa11f2000
}
}
....
}
What is the significance of 'refs' and weak' references here? Does refs=0 mean that there are no references to the object?
Thanks!
The control block for a shared_ptr holds two reference counts. One is the strong reference count, incremented once for each shared_ptr to the object. When it goes to zero the object is destroyed (and deallocated, unless it was allocated with make_shared).
The other is the weak reference count, incremented once as long as there are strong references, and again for each weak_ptr to the object. When it goes to zero, the control block is destroyed and deallocated.
So yes, in theory, the debugger display refs=0 means that there are no references to the object. Depending on where your program stopped, however, this sounds unlikely, in the program you show us, p lives until the end of main and should not give up its strong reference. It could be that the debugger's display is incorrect, especially if you compiled with optimizations.
Edit:
A weak count of 2 while refs is 0 means that all strong references are gone (all shared_ptr instances referring to the object have been destroyed), but there are 2 weak_ptrs remaining; unless the snapshot you're looking at is specifically within the destructor of the last shared_ptr, after decrementing the strong count but before decrementing the weak count, in which case it means there's only one weak_ptr left (which could be the one stored in enable_shared_from_this, if the snapshot is during your object's destructor).
Are you calling shared_from_this in Q's destructor and using the result without checking for null? shared_from_this doesn't work in constructors and destructors and always returns null. This could be the cause of the crash and would result in the values you observe.
From cppreference I learn that there is a constructor for std::shared_ptr:
template< class Y > explicit shared_ptr( Y* ptr );
And I tried a piece of code as follows:
#include <string>
#include <memory>
#include <iostream>
int main(void) {
/// block 1
{
std::shared_ptr<std::string> s1(new std::string("good"));
std::shared_ptr<std::string> s2(s1.get()); /// s2
std::cerr << s2.use_count() << std::endl;
}
/// block 2
{
std::shared_ptr<int> i1(new int(1));
std::shared_ptr<int> i2(i1.get()); /// i2
std::cerr << i2.use_count() << std::endl;
}
return 0;
}
It causes segment fault for block 1 but not for block 2, but both use_count are 1. The difference I can think about is that that int is a primitive type while std::string is managed by allocator.
I read bits/shared_ptr.h of gcc-4.9 and find that there is a post condition for this constructor:
use_count() == 1 && get() == __p
Question 1:
Should std::shared_ptr NOT constructed with a raw pointer that has been referenced by another smart pointer? In this sense, is the preferred way to use this constructor as follow?
std::shared_ptr<T>(new T(...));
Question 2:
Does the standard has explicit requirement for this constructor, or this post condition is ONLY for libstdc++?
Both of these cases are invalid uses of std::shared_ptr.
You cannot pass the same raw pointer to two std::shared_ptr constructors and expect well-defined results. Both std::shared_ptrs will believe that they own that pointer, and will attempt to delete it when they go out of scope.
This is a double free, and is invalid.
If you want to have two std::shared_ptrs that manage the same object, you can construct one of them with a raw pointer (or, better yet, use std::make_shared), and then copy construct/assign the second one from the first one. This way the memory will only be deallocated (and the destructor for the object fired) when the last of those std::shared_ptrs goes out of scope.
The reason you're getting a segmentation fault in the first case, and not the second one, is likely because int is a trivial type, and therefore you're not looking through a freed pointer to run the destructor of int, because it doesn't have one.
This is undefined behavior, we can see this by going to cppreference section for std::shared_ptr constructors says this (emphasis mine):
Constructing a shared_ptr using the raw pointer overload for an object
that is already managed by a shared_ptr leads to undefined behavior,
even if the object is of a type derived from
std::enable_shared_from_this (in other words, raw pointer overloads
assume ownership of the pointed-to object).
Both shared_ptr will attempt to delete the object they believe they now hold sole ownership for. We know this is undefined behavior by going to the draft C++ standard section 3.7.4.2 Deallocation functions which says:
If the argument given to a deallocation function in the standard
library is a pointer that is not the null pointer value (4.10), the
deallocation function shall deallocate the storage referenced by the
pointer, rendering invalid all pointers referring to any part of the
deallocated storage. Indirection through an invalid pointer value and
passing an invalid pointer value to a deallocation function have
undefined behavior. [...]
Ownership can only be shared by using copy construction or copy assignment. The same cppreference page provides a correct example using the copy constructor:
std::cout << "constructor with object\n";
std::shared_ptr<Foo> sh2(new Foo);
std::shared_ptr<Foo> sh3(sh2); // <- using copy construction
An alternative method to creating a shared_ptr is to use std::make_shared, which is safer:
Moreover, code such as f(std::shared_ptr(new int(42)), g()) can
cause a memory leak if g throws an exception because g() may be called
after new int(42) and before the constructor of shared_ptr. This
doesn't occur in f(std::make_shared(42), g()), since two function
calls are never interleaved.
As in title. This question probably already has an answer but I failed to find one.
The fundamental conceptual difference between a naked pointer and a weak_ptr is that if the object pointed to is destroyed, the naked pointer won't tell you about it. This is called a dangling pointer: a pointer to an object that doesn't exist. They're generally hard to track down.
The weak_ptr will. In order to use a weak_ptr, you must first convert it into a shared_ptr. And if that shared_ptr doesn't point to anything, then the object was deleted.
For example:
#include <iostream>
#include <memory>
std::weak_ptr<int> wp;
void test()
{
auto spt = wp.lock(); // Has to be copied into a shared_ptr before usage
if (spt) {
std::cout << *spt << "\n";
} else {
std::cout << "wp is expired\n";
}
}
int main()
{
{
auto sp = std::make_shared<int>(42);
wp = sp;
test();
}
test();
}
Output
42
wp is expired
A raw pointer is (at least normally) simply an address. You can't tell anything about what it points at from the pointer itself.
A weak_ptr is always associated with a shared_ptr, so we probably need to start with a shared_ptr to make any sense of a weak_ptr.
A shared_ptr is reference counted, so it keeps track of how many references (pointers) to an object exist, and automatically destroys the object when no more references to that object exist.
As I already said, a weak_ptr is associated with a shared_ptr. Unlike a shared_ptr, the existence of a weak_ptr does not increment the reference count for the pointee object. To use a weak_ptr, you must first convert it to a shared_ptr. If the current reference count is positive, that will succeed, and converting the weak_ptr to a shared_ptr will increment the reference count to signify that the converted pointer is a "real" reference to the object. If, on the other hand, the reference count is already zero (meaning the pointee object has already been destroyed) the attempt to convert the weak_ptr to a shared_ptr will simply fail.
A shared_ptr means shared ownership of the pointee object. The pointee object will remain in existence as long as at least one shared_ptr to that object exists, but as soon as the last shared_ptr to the object is destroyed, so will the pointee object.
A weak_ptr means non-owning access to the pointee object. It allows access if the object exists. If the object has been destroyed, it tells you that the pointee object no longer exists rather than attempting to access the destroyed object.