c++ - 为什么 std::enable_shared_from_this 允许多个 std::shared_ptr 实例?

标签 c++ c++17 shared-ptr weak-ptr enable-shared-from-this

有几个问题涉及 std::enable_shared_from_this 的行为,但我不认为这是重复的。

继承自std::enable_shared_from_this的类携带std::weak_ptr成员。当应用程序创建std::shared_ptr时指向 std::enable_shared_from_this 的子类,std::shared_ptr构造函数检查 std::weak_ptr ,如果未初始化,则对其进行初始化并使用 std::weak_ptr std::shared_ptr的控制 block 。但是,如果std::weak_ptr已经初始化,构造函数只是创建一个新的 std::shared_ptr带有一个新的控制 block 。当两个 std::shared_ptr 之一的引用计数达到时,这会将应用程序设置为崩溃。实例变为零并删除底层对象。

struct C : std::enable_shared_from_this<C> {};

C *p = new C();
std::shared_ptr<C> p1(p);

// Okay, p1 and p2 both have ref count = 2
std::shared_ptr<C> p2 = p->shared_from_this();

// Bad: p3 has ref count 1, and C will be deleted twice
std::shared_ptr<C> p3(p);

我的问题是:为什么图书馆会这样?如果std::shared_ptr构造函数知道该对象是 std::enable_shared_from_this子类并费心去检查 std::weak_ptr字段,为什么它不总是对新的 std::shared_ptr 使用相同的控制 block ,从而避免潜在的崩溃?

就此而言,为什么该方法shared_from_thisstd::weak_ptr 时失败成员未初始化,而不仅仅是初始化它并返回 std::shared_ptr

这个库的工作方式似乎很奇怪,因为它在很容易成功的情况下却失败了。我想知道是否存在我不理解的设计注意事项/限制。

我在 C++17 模式下使用 Clang 8.0.0。

最佳答案

如果我正确理解您的问题,您会认为第二次调用构造函数 shared_ptr 会在逻辑上重用存储在shared_from_this 中的控制 block 。

从您的角度来看,这看起来很合乎逻辑。让我们假设 C 是您正在维护的库的一部分,并且 C 的使用是您的库的用户的一部分。

struct C : std::enable_shared_from_this<C> {};

C *p = new C();
std::shared_ptr<C> p1(p);
std::shared_ptr<C> p3(p); // Valid given your assumption

现在,您找到了一种不再需要 enable_shared_from_this 的方法,并且在库的下一个版本中,该方法将更新为:

struct C {};

C *p = new C();
std::shared_ptr<C> p1(p);
std::shared_ptr<C> p3(p); // Now a bug

突然间,由于升级了库,完全有效的代码变得无效,没有任何编译器错误/警告。应尽可能避免这种情况。

同时,它也会造成很多困惑。因为根据您放入shared_ptr的类的实现,它是已定义的或未定义的行为。每次都将其设置为未定义会更容易混淆。

enable_shared_from_this 是在没有 shared_ptr 的情况下获取 shared_ptr 的标准解决方法。一个经典的例子:

 struct C : std::enable_shared_from_this<C>
 {
     auto func()
     {
         return std::thread{[c = this->shared_from_this()]{ /*Do something*/ }};
     }

     NonCopyable nc;
 };

添加您提到的额外功能确实会在您不需要时添加额外的代码,只是为了检查。不过,这并不是那么重要,零开销抽象并不是几乎零开销抽象。

关于c++ - 为什么 std::enable_shared_from_this 允许多个 std::shared_ptr 实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56465544/

相关文章:

c++ - 如何将 std::array 元素附加或插入 std::vector?

c++ - 在只读(即 const)访问器上执行结构化绑定(bind)的最佳实践是什么?

c++ - 如何在代码块中启用 c++17 支持

c++ - 使用 boost::make_shared 创建 vector 元素无法正常工作

c++ - 从具有 std::shared_ptr 的函数返回时,堆分配对象是否保证存活?

c++ - 设计 API 以接受通用输出流作为参数

c++ - 主函数外的新运算符 - C++

c++ - std::vector::insert 与 std::list::operator[]

c++ - Microsoft Visual Studio 2010 没有将构建工具放在 PATH 中以获得干净的目标?

c++ - 了解 Scott Meyers 的第三个 std::weak_ptr 示例