`weak_ptr:: expired` поведение в dtor объекта

Рассмотрим следующий код:

#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;
}

В этом коде я пытался выяснить, истек ли срок действия weak_ptr на фазе уничтожения объектов. Кажется так. Выход:

not expired
in dtor
expired

Я использовал gcc-5.1 с ideone.

Теперь у меня другая проблема. Я не мог найти документацию о том, что это стандартное поведение. Гарантировано ли это работать, всегда?

Ответ 1

Теперь у меня другая проблема. Я не мог найти документацию о том, что это стандартное поведение. Гарантировано ли это работать, всегда?

Нет. Действительно, это указано в стандарте, как поднято LWG issue 2751.

В стандарте С++ 14 нет языка, который гарантирует, что удаленный запуск, выполняемый shared_ptr, будет видеть все связанные с ним экземпляры weak_ptr, как истек. Например, стандарт не гарантирует, что утверждение в следующем фрагменте не будет срабатывать:

std::weak_ptr<Foo> weak;
std::shared_ptr<Foo> strong{
  new Foo,
  [&weak] (Foo* f) {
    assert(weak.expired());
    delete f;
  },
};

weak = strong;
strong.reset();

Кажется очевидным, что цель состоит в том, что связанный weak_ptr истек, потому что в противном случае shared_ptr удалители могли бы воскресить ссылку на объект, который удаляется.

Рекомендуемое исправление: 23.11.3.2 [util.smartptr.shared.dest] следует указать, что уменьшение use_count(), вызванное деструктором секвентируется перед вызовом делетера или вызовом delete p.

Текущая формулировка для ~shared_ptr(), как указано выше, просто указывает, что вызывается делектор, с ненормативной запиской о том, что количество экземпляров, разделяющих владение, уменьшается.

Хотя намерение, вероятно, состоит в том, что weak.expired(), когда вызывается делетер, сомнительно полагаться на это. Действительно разумно с уверенностью утверждать, что shared_ptr больше не разделяет собственность после ее уничтожения - вопрос о том, что вопрос во время уничтожения немного странный.

Ответ 2

Использование make_shared похоже на создание объекта с предоставленным конструктором по умолчанию.

template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );

Создает объект типа T и обертывает его в std::shared_ptrиспользуя args в качестве списка параметров для конструктора T. Объект строится как бы по выражению (std:: make_shared)

После анонимного объема в главном. Общий ptr будет удален.

Объект уничтожается, и его память освобождается, когда любой из происходит следующее:

уничтожается последний оставшийся shared_ptr, владеющий объектом; (std:: shared_ptr)

.

Деструктор shared_ptr уменьшает количество общих владельцев блок управления. Если этот счетчик достигнет нуля, блок управления вызывает деструктор управляемого объекта. Блок управления не освободить себя, пока счетчик std:: weak_ptr не достигнет нуля, поскольку Что ж. std:: shared_ptr Замечания по внедрению

Это означает, что ваш объект будет вызывать его деструктор после того, как началось уничтожение последнего общего ptr. Выход:

not expired
in dtor
expired

- ожидаемое поведение.

Ответ 3

[C++11: 20.7.2.3.5]

long use_count() const noexcept;

1 Возвращает: 0, если *this пуст; в противном случае количество экземпляров shared_ptr, которые разделяют право собственности на *this.
2 [Примечание: use_count() не обязательно эффективен. -end note]

bool expired() const noexcept;

3 Возвращает: use_count() == 0.
4 [Примечание: expired() может быть быстрее, чем use_count(). -end note]

Теперь нам нужно собрать несколько битов (и я не буду приводить их здесь), но weak_ptr пуст только при построении как таковой, поэтому мы решили решить, количество экземпляров shared_ptr, которые разделяют право собственности на *this ", уже равно нулю.

Наиболее подходящая формулировка, которую я могу найти, заключается в том, должен ли счет использования быть ненулевым до тех пор, пока уничтоженный объект управления не будет уничтожен:

[C++11: 20.7.2.2.2/2]: [Примечание: поскольку разрушение *this уменьшает количество экземпляров, которые разделяют право собственности на *this на единицу, после *this было уничтожено все экземпляры shared_ptr, которые разделяют право собственности на *this сообщит о use_count(), который меньше, чем его предыдущее значение. -end note]

Обратите внимание на слово "после".

Тем не менее, формулировка довольно пушистая и на самом деле не дает каких-либо определенных гарантий последовательности (то есть, она не говорит "только после" ), поэтому ваш пробег может варьироваться и hellip; в частности, поскольку моя интерпретация идентифицирует ваше поведение GCC как ошибку.

Ответ 4

Не сам стандарт, но:

http://en.cppreference.com/w/cpp/memory/weak_ptr/expired

Проверяет, удален ли управляемый объект. эквивалент to use_count() == 0.

Итак, вопрос о погоде use_count устанавливается равным 0 до или после удаления. Теперь нет в этом в проекте стандарта: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf [страница 566 20.9.2.2.2]

~shared_ptr();

Эффекты:

  • Если *this пуст или имеет право собственности на другой экземпляр shared_ptr (use_count() > 1), побочных эффектов нет.
  • В противном случае, если *this принадлежит объект p и вызывается deleter d, d(p).
  • В противном случае *this принадлежит указатель p и вызывается delete p.

[Примечание: поскольку разрушение *this уменьшает количество экземпляры, которые разделяют право собственности на *this на один, после *this имеет были уничтожены все экземпляры shared_ptr, которые *this сообщит use_count(), который меньше, чем предыдущий стоимость. - конечная нота]

Ответ 5

A weak_ptr истекает, когда больше нет shared_ptr, ссылающегося на объект.

Когда (сразу после) последний shared_ptr перестает ссылаться на уничтоженный объект.

В этот момент нет ссылки shared_ptr, поэтому любой weak_ptr истек.

Теперь деструктор объекта вызывается и, если он имеет отдельное хранилище (т.е. не создан с помощью make_shared), его хранилище освобождается.

Блок управления, в котором хранятся функция отсчета ссылок и необработанный указатель и функция удаления, сохраняется, если есть какой-либо weak_ptr, ссылающийся на него. Когда последний weak_ptr перестает ссылаться на него, также блокируется и освобождается блок управления. 1.e., shared_ptr сохраняют объект живым вместе с его блоком управления, а экземпляры weak_ptr сохраняют блок управления.