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.
Related
Let's say I have two classes, Base and Derived, where Derived inherits from Base. Now, let's say I execute the following code:
shared_ptr<Derived> derivedPtr = make_shared<Derived>();
shared_ptr<Base> basePtr = derivedPtr;
Will the copying of derivedPtr to basePtr result in derivedPtr's reference count being updated (so that derivedPtr.use_count() and basePtr.use_count() equal 2)? Or, since the two instances of shared_ptr are different types, will the two have a separate reference count that isn't shared (so that derivedPtr.use_count() and basePtr.use_count() equal 1)?
So shared_ptr is more than just a pointer and a reference count.
It is a pointer and a pointer to a control block. That control block contains a strong count, a weak count, and a destruction function.
There are 3 ways to construct a shared_ptr.
First, you can construct it from a raw pointer. When that happens, it allocates a control block and sticks a "destroyer" function into it to destroy the raw pointer memory (delete t;).
Second, you can use make_shared. This allocates one block with space for both the control block and the object in it. It then sets the destroyer up to just destroy the object, and not recycle the memory. The destructor of the control block cleans up both memory allocations.
Third, there is the aliasing constructors. These share control blocks (and hence destruction code), but have a different object pointer.
The most common aliasing constructor is the one that creates a pointer-to-base, which you are doing above. The pointer-to-base differs from the shared ptr you created it from, but the control block remains the same. So whenever the control block hits 0 strong reference counts, it destroys the object as its original derived object.
The rarer one can be used to return shared pointers to member variables, like this:
struct Bob {
int x;
};
auto pBob = std::make_shared<Bob>();
pBob->x = 7;
auto pInt = std::shared_ptr<int>( pBob, &(pBob->x) );
now pInt is a pointer to pBob->x that shares the reference counting of the Bob created 2 lines above (where we made pBob).
pBob = {};
now the last pointer to the Bob is gone, but the object survives, kept alive by the pInt's control block (and strong count) ownership.
Then when we:
pInt = {};
finally the Bob is deallocated.
The cast-to-base implicit conversion you did in your question is just a variation of this.
This second aliasing constructor can also be used to do extremely strange things, but that is another topic.
shared/weak ptr is one of those cases where it seems you can just "monkey code" it without understanding it, but in my experience using shared ownership is sufficiently hard that fully understanding shared ptr is (a) easier than getting shared ownership right, and (b) makes getting shared ownership right easier.
According to the first answer in this article: Explicitly deleting a shared_ptr
Is it possible to force delete a std::shared_ptr and the object it manages like below code?
do {
ptr.reset();
} while (!ptr.unique());
ptr.reset(); // To eliminate the last reference
Technically, this should try calling std::shared_ptr::reset if the pointer has more than 1 reference count, unless it reaches to one. Any thoughts on this?
This code doesn't make any sense.
Once you reset ptr, it doesn't manage an object anymore. If ptr was the only shared_ptr sharing ownership, then you're done. If it wasn't... well, you don't have access to all those other ones. Calling reset() on a disengaged shared_ptr is effectively a noop - there's nothing more to reset.
Imagine a simple scenario:
std::shared_ptr<int> a = std::make_shared<int>(42);
std::shared_ptr<int> b = a; // a and b are sharing ownership of an int
do {
a.reset();
} while (!a.unique());
The only way to reset b is to reset b - this code will reset a only, it cannot possibly reach b.
Also note that unique() was deprecated in C++17 and is removed entirely in C++20. But if even if you use use_count() instead, once you do a.reset(), a.use_count() will be equal to 0 because a no longer points to an object.
No this is not possible (or desirable). The point of a shared pointer is that if you have one you can guarantee the object it points to (if any) will not disappear from under you until (at least) you have finished with it.
Calling ptr.reset() will only reduce the reference count by 1 - being your shared pointer's reference. It will never affect other references from other shared pointers that are sharing your object.
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.
I just stumbled on the boost::shared_ptr documentation, which goes:
Sometimes it is necessary to obtain a shared_ptr given a raw pointer
to an object that is already managed by another shared_ptr instance.
Example:
void f(X * p)
{
shared_ptr<X> px(???);
}
Inside f, we'd like to create a shared_ptr to *p.
In the general case, this problem has no solution.
Why? Is it not allowed to do something like:
shared_ptr<X> px(p);
Am I missing something?
If you have a shared_ptr managing a pointer and then create another shared_ptr managing the same pointer (NOT copying the original shared_ptr), you end up with two managers for the same resource. When one of the two reach a reference count of 0, it will delete the object and the other shared_ptr will point to deleted memory with all that follows.
If you would do this:
main() {
Object *obj = new Object();
func(obj)
}
void func( Object *obj ) {
shared_ptr objPtr(obj); // take ownership.
objPtr->fun();
// Passed object "obj" is destroyed here.
}
At the end of the function func the object pointer would get destroyed, and with the pointer the object itself. This wouldn't be a desirable behaviour.
Actually, you can do that, but you must know that the pointed object will be deleted when exiting the function...
I tried with boost:
void f(X * p)
{
boost::shared_ptr<X> px(p);
// do smething
}
void main()
{
X* ptr = new X();
f( ptr );
// ptr is not valid anymore because the object were deleted
}
Jean
You could do it, but it could lead to undefined behavior, since there is no way to tell the second shared pointer that the reference count (the number of shared pointers pointing at the same object) increased. Then things like this could happen:
void f()
{
boost::shared_ptr<int> firstSmart(new int(23)); // firstSmart is the only
// manager of the int
int *raw = firstSmart.get();
boost::shared_ptr<int> secondSmart(raw); // secondSmart also manages
// the same int as firstSmart
// but secondSmart does not
// know about firstSmart
// and vice versa
}
when f exits secondSmart gets destroyed, destroying the shared int. Then firstSmart gets destroyed and attempts to destroy the already destroyed int thus leading to undefined behaviour.
Its not possible to do this.
Shared pointers work using reference counting. When you assign a resource (raw pointer) to a shared pointer a reference count object is created with count =1. When another shared pointer is created for the same resource the reference count object (which is shared between both the shared pointers) is updated to value count =2. If one shared pointer is deleted the count in the shared reference object is decremented and when it reached 0 the resource is destroyed.
For the above mechanism to work the first shared pointer should be created using something like shared_ptr px(p) and all subsequent ones using px (not 'p'). This way all the shared pointers created will know that they are holding same resource and share the same reference count object.
If you created another shared pointer using shared_ptr px(p) then you end up with two shared pointer not related to each other - i.e there reference count objects are not same. They both assume that they are holding distinct resource and each of them have distinct (different) reference count object with count =1. (You don’t want this).
As per my understanding, weak pointer is used to cyclic dependency problem occurs if we use all shared_ptr objects and if there is a cyclic dependency. Weak pointers are used to break the cycles. weak pointers achieves this by using lock() which will create shared pointer.
class A { shared_ptr<B> b; ... };
class B { weak_ptr<A> a; ... };
shared_ptr<A> x(new A); // +1
x->b = new B; // +1
x->b->a = x; // No +1 here
But now assume that I created lock calling x->b->a.lock(), so ref count of x will become 2. and if x leaves the scope, still there will be memory leak right? Because I created a shared pointer using lock() and ref count became 2. Please let me know whether my understanding is correct or not.
There are two distinct reference counts involved for a shared_ptr shared object:
The number of references to the object, i.e. shared_ptr instances.
The number of references to the control block, i.e. shared_ptr and weak_ptr instances.
A weak_ptr contributes only to the latter count. When all shared_ptr instances have been destroyed, the object deleter is called, which usually is the default one that destroys the object. The control block still exists if there are weak pointers. When also all weak pointers have been destroyed, the control block is destroyed.
So (ignoring possible optimization of caching object pointer directly in each shared_ptr instance), in your case you have x pointing (hidden for you) to the control block, which has a pointer to the A instance. And you have the b member of that instance pointing to a second control block, which has a pointer to a B instance. Finally that instance has a pointer to the control block that x points to, which is circular yes, but not a circularity of ownership.