问题
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_ptr
s 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?
回答1:
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 associatedweak_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_ptr
s are expired, because otherwiseshared_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 todelete 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.
回答2:
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.
回答3:
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 anothershared_ptr
instance (use_count() > 1), there are no side effects.- Otherwise, if
*this
owns an objectp
and a deleterd
,d(p)
is called.- Otherwise,
*this
owns a pointerp
, anddelete 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 allshared_ptr
instances that shared ownership with*this
will report ause_count()
that is one less than its previous value. — end note]
回答4:
A weak_ptr
expires when there are no more shared_ptr
s 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_ptr
s 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_ptr
s 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.
来源:https://stackoverflow.com/questions/41851520/weak-ptrexpired-behavior-in-the-dtor-of-the-object