Сравните с: std::shared_ptr, который пуст, но не равен нулю
Конструктор псевдонимов std::shared_ptr<T>
позволяет нам играть в интересные игры. В приведенном выше сообщении SO обсуждается, когда первым аргументом является std::shared_ptr<void>{nullptr}
. Меня интересует обратная сторона. Гарантировано ли, что указанный объект останется живым, даже если shared_ptr
"является" nullptr
(и был бы полностью недоступен, если бы мы не сохраняли ссылку на него)?:
std::shared_ptr<S> ps = std::make_shared<S>();
auto& s = *ps; // Keep a reference to the S.
auto p = std::shared_ptr<S>(ps, nullptr); // Aliasing c'tor with null pointer.
ps = nullptr;
assert(ps == nullptr);
assert(p == nullptr);
foo(s); //< Is the S still alive here?
Да, объект S
все еще жив, когда вызывается foo()
, поэтому ваша ссылка s
все еще действительна.
Конструктор псевдонимов p
будет увеличивать счетчик ссылок объекта S
, который в данный момент содержит ps
, сохраняя этот объект живым, в то время как p
удерживает nullptr
, который вы дали его конструктору. Когда вы закончите использовать p
, он уменьшит количество ссылок.
Вы можете проверить это, запросив refcount:
std::shared_ptr<S> ps = std::make_shared<S>();
auto& s = *ps; // Keep a reference to the S.
auto p = std::shared_ptr<S>(ps, nullptr); // Aliasing c'tor with null pointer.
ps = nullptr;
assert(ps == nullptr);
assert(p == nullptr);
cout << ps.use_count() << endl; // prints 0
cout << p.use_count() << endl; // prints 1
foo(s); //< S is still alive here
Если вы удалите оператор ps = nullptr;
, вы увидите, что и ps
, и p
вместо этого сообщают, что счетчик ссылок равен 2:
std::shared_ptr<S> ps = std::make_shared<S>();
auto& s = *ps; // Keep a reference to the S.
auto p = std::shared_ptr<S>(ps, nullptr); // Aliasing c'tor with null pointer.
//ps = nullptr;
assert(ps != nullptr);
assert(p == nullptr);
cout << ps.use_count() << endl; // prints 2
cout << p.use_count() << endl; // prints 2
foo(s); //< S is still alive here
Да, «нулевой, но не пустой» shared_ptr
сохранит объект живым, потому что он разделяет право собственности с shared_ptr
, из которого он был создан. Все shared_ptr
, которые совместно владеют друг другом, вносят свой вклад в атомарный счетчик ссылок, хранящийся в блоке управления, и только когда этот счетчик ссылок достигает нуля, принадлежащий объект уничтожается.
Для стандартного языка см. [util.smartptr.shared.const]/14:
template<class Y> shared_ptr(const shared_ptr<Y>& r, element_type* p) noexcept;
Создает экземплярshared_ptr
, который хранитp
и разделяет право собственности сr
На значение p
не наложено никаких ограничений; поэтому это может быть любое допустимое значение указателя, включая нуль или даже указатель за концом (хотя я не уверен, почему вы хотите это сделать).
Затем см. [util.smartptr.shared.dest]/(1.1):
Если
*this
пуст или владеет другим экземпляромshared_ptr
(use_count() > 1
), побочные эффекты отсутствуют.
Другими словами, когда ps
уничтожается, он по-прежнему разделяет право собственности с p
, поэтому объект еще не уничтожен.
Полученный объект на самом деле не является недостижимым в обычном смысле, поскольку его все же можно уничтожить. Вы просто не можете сделать что-нибудь еще с ним.