繁体   English   中英

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

[英]Why does std::enable_shared_from_this allow multiple std::shared_ptr instances?

有几个问题涵盖了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 但是,如果已经初始化了std::weak_ptr ,则构造函数将使用新的控制块创建一个新的std::shared_ptr 当两个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::enable_shared_from_this检查std::weak_ptr字段,为什么它不总是对新的std::shared_ptr使用相同的控制块,从而避免了潜在的崩溃?

因此,为什么在未初始化std::weak_ptr成员而不是仅对其进行初始化并返回std::shared_ptr时,方法shared_from_this失败了?

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

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

如果我正确理解了您的问题,则可以假定第二次调用构造函数shared_ptr在逻辑上将重用存储在shared_from_this中的控制块。

从您的角度来看,这看起来合乎逻辑。 让我们暂时假设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;
 };

添加您提到的额外功能确实会在不需要时添加额外的代码,仅用于检查。 但是,零开销抽象并不是将零开销抽象几乎是那么重要。

这不是问题的答案,而是更多基于用户jvapen对这个问题的回答。

您已经在回答中说明了这一点:

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

我在这里看不到的是第5行std::shared_ptr<C> p3(p); 现在是一个错误。 根据cppreference:sh​​ared_ptr,他们专门指出:

std::shared_ptr是一个智能指针,它通过指针保留对象的共享所有权。 几个shared_ptr对象可能拥有同一对象。

创建两个指向同一指针的shared_ptr是未定义的行为,与std::enable_shared_from_this 您的代码应为:

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

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

std::shared_ptr<C> p2 = p->shared_from_this();

std::shared_ptr<C> p3(p1);

创建实际上没有所有权的辅助智能指针(在重置/销毁最后一个副本时不执行任何操作),或者在控制块中(在Deleter对象中)携带原始智能指针的副本,以便在辅助引用时count变为零时,主要refcount递减,这是一种非常罕见的情况,对于大多数程序员来说可能会造成混淆,但是它并不是固有的非法行为。 (我认为,在特殊情况下,可以为这种模式提供充分的理由。)

在另一方面,存在shared_from_this有力地表明,只有一个拥有shared_ptr ,所以有shared_from_this时的多组可能应该避免std::shared_ptr预期。 自我引用非拥有指针的显式管理更安全,因为它使此类问题在用户代码中显而易见,这与std::enable_shared_from_this的隐式行为不同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM