首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么std::enable_shared_from_this允许多个std::shared_ptr实例?

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

Stack Overflow用户
提问于 2019-06-06 01:44:13
回答 3查看 386关注 0票数 2

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

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

代码语言:javascript
复制
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使用相同的控制块,从而避免潜在的崩溃呢?

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

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

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-06-06 03:14:32

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

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

代码语言:javascript
复制
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的方法,并且在您的库的下一个版本中,它将更新为:

代码语言:javascript
复制
struct C {};

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

突然之间,完全有效的代码变得无效,没有任何编译器错误/警告,因为升级了你的库。在可能的情况下,应该防止这种情况发生。

同时,它也会造成很多混乱。原因取决于您放在shared_ptr中的类的实现,它要么是定义的行为,要么是未定义的行为。每次都将其设置为未定义,这样就不那么容易混淆了。

如果您没有shared_ptrenable_shared_from_this是获得shared_ptr的标准解决方法。一个经典的例子:

代码语言:javascript
复制
 struct C : std::enable_shared_from_this<C>
 {
     auto func()
     {
         return std::thread{[c = this->shared_from_this()]{ /*Do something*/ }};
     }

     NonCopyable nc;
 };

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

票数 5
EN

Stack Overflow用户

发布于 2019-06-06 01:55:31

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

代码语言:javascript
复制
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);
票数 -1
EN

Stack Overflow用户

发布于 2019-06-09 13:08:21

创建一个实际上不拥有的辅助智能指针(当最后一个副本被重置/销毁时,它什么也不做),或者在控制块中携带原始智能指针的副本(在deleter对象中),以便当辅助引用计数变为零时,主引用计数递减,这是非常罕见的情况,可能会使大多数程序员感到困惑,但它本身并不是非法的。(我认为在特殊情况下可以很好地支持这种模式。)

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

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56465544

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档