std::shared_ptr 为空但不为空

std::shared_ptr which is null but not empty

比较:

std::shared_ptr<T> 别名构造函数让我们玩有趣的游戏。上面的 SO post 讨论了第一个参数是 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?

https://godbolt.org/z/M19s54

是的,当调用 foo() 时,S 对象仍然存在,因此您的 s 引用仍然有效。

p 别名构造函数 将增加 ps 当前持有的 S 对象的引用计数,使该对象保持活动状态,而 p 持有您为其构造函数提供的 nullptr 。当您使用完 p 后,它会减少引用计数。

您可以通过查询引用计数来验证这一点:

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

Demo

如果删除 ps = nullptr; 语句,您将看到 psp 都报告引用计数为 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

Demo

是的,“null 但不为空”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;
Constructs a shared_ptr instance that stores p and shares ownership with r

p的值没有指定约束;因此,它可以是任何有效的指针值,包括 null 甚至是尾后指针(尽管我不确定您为什么要这样做)。

然后看[util.smartptr.shared.dest]/(1.1):

If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.

也就是说,当ps被销毁时,它仍然与p共享所有权,所以对象还没有被销毁。

生成的对象并不是通常意义上的真正不可访问,因为它仍然有可能销毁它。你不能用它做任何其他事情。