`weak_ptr::expired` behavior in the dtor of the ob

2019-03-24 06:05发布

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?

4条回答
Emotional °昔
2楼-- · 2019-03-24 06:11

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楼-- · 2019-03-24 06:17

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.

查看更多
老娘就宠你
4楼-- · 2019-03-24 06:23

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]

查看更多
爷、活的狠高调
5楼-- · 2019-03-24 06:24

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.

查看更多
登录 后发表回答