2

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

继承自的类std::enable_shared_from_this带有一个std::weak_ptr成员。当应用程序创建一个std::shared_ptr指向 的子类的指针时std::enable_shared_from_thisstd::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::weak_ptr字段,为什么它不总是为 new 使用相同的控制块std::shared_ptr,从而避免潜在的崩溃?

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

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

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

4

4 回答 4

5

如果我正确理解您的问题,您会假​​设第二次调用构造函数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 的类的实现,它是已定义或未定义的行为。每次都将其设为 undefined 会更容易混淆。

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;
 };

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

于 2019-06-05T19:14:31.760 回答
0

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

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

struct C {};

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

What I'm not seeing here is how line 5 std::shared_ptr<C> p3(p); is now a bug. According to cppreference:shared_ptr they specifically state this:

std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object.

于 2019-06-07T13:29:53.173 回答
-1

为同一个指针创建两个shared_ptrs 是未定义的行为,与 . 无关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-05T17:55:30.737 回答
-1

Creating a secondary smart pointer that is either actually non owning (it does nothing when the last copy is reset/destroyed), or carries a copy of the original smart pointer in the control block (in the deleter object) such that when the secondary reference count goes to zero the primary refcount is decremented, is a very rare occurrence and would probably be confusing for most programmers, but it isn't inherently illegal. (And I think one can make a strong case for that pattern in special cases.)

另一方面,存在shared_from_this强烈表明只有一个拥有shared_ptr,因此shared_from_this当期望多组时应该避免拥有std::shared_ptr。与std::enable_shared_from_this.

于 2019-06-09T05:08:20.920 回答