有几个问题涵盖了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
实例之一的引用计数变为零时崩溃,并删除底层对象。
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。
发布于 2019-06-06 03:14:32
如果我正确理解了您的问题,您可能会认为第二次调用构造函数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中的类的实现,它要么是定义的行为,要么是未定义的行为。每次都将其设置为未定义,这样就不那么容易混淆了。
如果您没有shared_ptr
,enable_shared_from_this
是获得shared_ptr
的标准解决方法。一个经典的例子:
struct C : std::enable_shared_from_this<C>
{
auto func()
{
return std::thread{[c = this->shared_from_this()]{ /*Do something*/ }};
}
NonCopyable nc;
};
添加你提到的额外功能确实会在你不需要的时候添加额外的代码,只是为了检查。不过,这并不重要,零开销抽象并不是几乎零开销抽象。
发布于 2019-06-06 01:55:31
创建两个指向同一指针的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);
发布于 2019-06-09 13:08:21
创建一个实际上不拥有的辅助智能指针(当最后一个副本被重置/销毁时,它什么也不做),或者在控制块中携带原始智能指针的副本(在deleter对象中),以便当辅助引用计数变为零时,主引用计数递减,这是非常罕见的情况,可能会使大多数程序员感到困惑,但它本身并不是非法的。(我认为在特殊情况下可以很好地支持这种模式。)
另一方面,存在shared_from_this
强烈表明只有一个拥有shared_ptr
,因此当预期有多组shared_from_this
时,可能应该避免使用std::shared_ptr
。与std::enable_shared_from_this
的隐式行为不同,显式管理自引用非所属指针更安全,因为它使此类问题在用户代码中变得明显。
https://stackoverflow.com/questions/56465544
复制相似问题