【问题标题】:std::enable_shared_from_this; public vs privatestd::enable_shared_from_this;公共与私人
【发布时间】:2017-02-17 15:01:19
【问题描述】:

我使用 shared_ptr 和 enable_shared_from_this 玩了一会儿,但遇到了一些我不太了解的东西。

在我的第一次尝试中,我构建了这样的东西:

class shared_test : std::enable_shared_from_this<shared_test> {
public:
    void print(bool recursive) {
        if (recursive) {
            shared_from_this()->print(false);
        }

        std::cout << "printing" << std::endl;
    }
};

请注意,这个类正在私下扩展 std::enable_shared_from_this。这显然有很大的不同,因为执行这样的事情:

int main() {
    auto t(std::make_shared<shared_test>());
    t->print(true);
    return 0;
}

抛出一个 bad_weak_ptr 异常。好像我将类定义更改为从 std::enable_shared_from_this 公开的固有的,这只是查找。

为什么会这样,我在这里想念什么?并且没有办法让它适用于私有继承,因为 shared_test 类的“外部世界”不需要知道它正在启用共享......(至少,如果你问我,还是我又错过了什么?)

【问题讨论】:

    标签: c++ c++11 shared-ptr private-inheritance enable-shared-from-this


    【解决方案1】:

    为什么会这样,我在这里想念什么?

    要让shared_from_this 工作,enable_shared_from_this 必须知道拥有该课程的shared_ptr。在您的 STL 实现中,它是weak_ptr,通过其他实现是可能的。当您私有继承时,就不可能从您的类外部访问基类的属性。实际上,甚至不可能理解你是从那里继承下来的。所以make_shared 生成通常的shared_ptr 初始化,而没有在enable_shared_from_this 中设置适当的字段。

    异常不是来自make_shared,而是来自shared_from_this,因为enable_shared_from_this没有正确初始化。

    还有没有办法让它适用于私有继承,因为 shared_test 类的“外部世界”不需要知道它正在启用共享...

    没有。外部世界必须知道该对象与 shared_ptr 有特殊关系才能正常工作。

    【讨论】:

    • 有趣的是,该错误是运行时错误,而不是私有访问编译错误。在我能想到的所有其他情况下,布局,分辨率,......绝对没有区别。访问控制是最后独立完成的,直到#define private public 给出相同的目标文件。
    • @JohanLundberg 对于outside world,私有继承和无继承之间没有(逻辑)区别。私有继承意味着is_base_of&lt;enable_shared_from_this&lt;shared_test&gt;, shared_test&gt; 为假,这是一个编译时测试,它控制shared_ptr 构造函数生成的代码。
    • @Oktalist。谢谢 - 我跟着。我在评论中写的显然不适合继承。
    • @JohanLundberg 异常不是来自make_shared,而是来自shared_from_this。原因很简单:因为私有继承make_shared没有看到你的类是基于enable_shared_from_this的。因此,没有为位于enable_shared_from_this 中的weak_ptr 的初始化生成代码。它只是保存它的默认未初始化值。当您调用 shared_from_this 时,它会查看 weak_ptr 并抛出异常。
    • @Oktalist "私有继承和无继承之间没有(逻辑)区别" 这是不正确的:D 的私有继承来自 B 允许从D*B*,所以差别很大。
    【解决方案2】:

    没有办法让它适用于私有继承,因为 shared_test 类的“外部世界”不需要知道它正在启用共享

    shared_ptr 本身就是“外部世界”的一部分; shared_ptr 构造函数需要能够访问它指向的shared_test 对象的enable_shared_from_this 基类子对象,以便初始化enable_shared_from_this 实现的私有weak_ptr 成员。

    【讨论】:

      【解决方案3】:

      我从 STL 中的代码分析这个问题:

      自动 t(std::make_shared());

      代码行构造一个shared_ptr;首先我们深入make_shared函数

       // FUNCTION TEMPLATE make_shared
         template<class _Ty,
         class... _Types>
         NODISCARD inline shared_ptr<_Ty> make_shared(_Types&&... _Args)
         {    // make a shared_ptr
           const auto _Rx = new _Ref_count_obj<_Ty>(_STD forward<_Types>(_Args)...);
      
           shared_ptr<_Ty> _Ret;
          _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Getptr(), _Rx);
           return (_Ret);
         }
      

      注意:我们深入了解函数_Ret.Set_ptr_rep_and_enable_shared。我们可以看到如下:

      template<class _Ux>
         void _Set_ptr_rep_and_enable_shared(_Ux * _Px, _Ref_count_base * _Rx)
         {    // take ownership of _Px
              this->_Set_ptr_rep(_Px, _Rx);
              _Enable_shared_from_this(*this, _Px);
         }
      

      所以我们找到函数 _Enable_shared_from_this ,继续:

       template<class _Other,
          class _Yty>
          void _Enable_shared_from_this(const shared_ptr<_Other>& _This, _Yty * _Ptr)
          {   // possibly enable shared_from_this
          _Enable_shared_from_this1(_This, _Ptr, _Conjunction_t<
              negation<is_array<_Other>>,
              negation<is_volatile<_Yty>>,
              _Can_enable_shared<_Yty>>{});
         }
      

      我们找到一个关键点:_Can_enable_shared<_yty>

      template<class _Yty,
          class = void>
          struct _Can_enable_shared
              : false_type
          {   // detect unambiguous and accessible inheritance from enable_shared_from_this
          };
      
      template<class _Yty>
          struct _Can_enable_shared<_Yty, void_t<typename _Yty::_Esft_type>>
              : is_convertible<remove_cv_t<_Yty> *, typename _Yty::_Esft_type *>::type
          {   // is_convertible is necessary to verify unambiguous inheritance
          };
      

      我们发现只有 _Yty 有 _Esft_type 并且 _Yty 可以转换为 _Esft_type,可以 _Yty 可以启用_shared(如果想了解更多,那就是在 _Yty 中设置weak_ptr,否则在使用 shared_from_this 时可能会出现 bad_weak_ptr 错误)。 那么_Esft_type是什么?

       template<class _Ty>
          class enable_shared_from_this
          {   // provide member functions that create shared_ptr to this
      public:
          using _Esft_type = enable_shared_from_this;
           ...
         }
      

      所以_Esft_type只是表示enable_shared_from_this<_ty>,所以如果你使用私有继承,外面不仅看不到_Esft_type而且_Yt不能转换成_Esft_type。所以weak_ptr不能被设置,所以bad_weak_ptr可能会被调用。

      所以外部需要知道_Esft_type的存在,所以在构造shared_ptr时,也可以设置shared_test的weak_ptr。

      【讨论】:

        【解决方案4】:

        根据文档,为了“shared_from_this”成员函数的可访问性,必须公开继承。

        “从 std::enable_shared_from_this 公开继承为类型 T 提供了一个成员函数 shared_from_this” - 来自 CPP 参考 http://en.cppreference.com/w/cpp/memory/enable_shared_from_this

        shared_from_this:

        返回一个共享 *this 所有权的 shared_ptr (公共成员函数)

        【讨论】:

        • 您的回答实际上并没有为问题添加任何新内容:shared_from_this 必须公开继承。有趣的部分是为什么会有这样的限制。
        • “enable_shared_from_this”应该被继承.....而不是“shared_from_this”。而“shared_from_this”是基类中的一个函数,它返回“*this”指针。请注意,类名和函数名之间多了一个“enable_”字样。
        • 你知道的没错....基类的受保护和公共成员成为派生类的私有成员。因此,其他类不能通过 Derived 类对象访问 Base 类的任何成员,因为它们在 Derived 类中是私有的。
        • 仔细阅读我的回答中的“为了便于访问”这个词。它会解释我的答案。
        猜你喜欢
        • 2014-07-14
        • 2013-01-31
        • 1970-01-01
        • 2020-11-11
        • 2015-12-11
        • 2012-08-03
        • 2011-01-09
        • 1970-01-01
        • 2014-01-17
        相关资源
        最近更新 更多