问题 的std :: enable_shared_from_this;公共与私人


我正在玩一些使用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中将类定义更改为公开的,这就是find。

为什么,我在这里想念什么?并没有一种方法可以使它适用于私有继承,因为shared_test类的“外部世界”不需要知道它是否允许共享...(至少,如果你问我,还是我又想念一些东西?)


3124
2017-10-08 20:34


起源



答案:


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

要做 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有特殊关系才能正常工作。


8
2017-10-08 20:40



有趣的是,错误是运行时错误而不是私有访问编译错误。在我能想到的所有其他情况下,布局,重新安排,......完全没有区别。访问控制是最后一个独立完成的,直到#define private public给出相同的目标文件。 - Johan Lundberg
@JohanLundberg为 outside world,私有继承和没有继承之间没有(逻辑)区别。私人继承意味着 is_base_of<enable_shared_from_this<shared_test>, shared_test> 是false,这是一个编译时测试,它控制由哪个代码生成 shared_ptr 构造函数。 - Oktalist
@Oktalist。谢谢 - 我跟着。我在评论中写的内容显然不适用于继承。 - Johan Lundberg
@JohanLundberg异常不是从中抛出的 make_shared 但形式 shared_from_this。原因很简单:因为私有继承 make_shared 没看到你的班级是基于 enable_shared_from_this。因此没有为初始化生成代码 weak_ptr 位于 enable_shared_from_this。它只是保持默认的未初始化值。你打电话时 shared_from_this 它看起来就是这样 weak_ptr 并抛出异常。 - Alexey Guseynov


答案:


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

要做 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有特殊关系才能正常工作。


8
2017-10-08 20:40



有趣的是,错误是运行时错误而不是私有访问编译错误。在我能想到的所有其他情况下,布局,重新安排,......完全没有区别。访问控制是最后一个独立完成的,直到#define private public给出相同的目标文件。 - Johan Lundberg
@JohanLundberg为 outside world,私有继承和没有继承之间没有(逻辑)区别。私人继承意味着 is_base_of<enable_shared_from_this<shared_test>, shared_test> 是false,这是一个编译时测试,它控制由哪个代码生成 shared_ptr 构造函数。 - Oktalist
@Oktalist。谢谢 - 我跟着。我在评论中写的内容显然不适用于继承。 - Johan Lundberg
@JohanLundberg异常不是从中抛出的 make_shared 但形式 shared_from_this。原因很简单:因为私有继承 make_shared 没看到你的班级是基于 enable_shared_from_this。因此没有为初始化生成代码 weak_ptr 位于 enable_shared_from_this。它只是保持默认的未初始化值。你打电话时 shared_from_this 它看起来就是这样 weak_ptr 并抛出异常。 - Alexey Guseynov


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

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


5
2017-10-08 20:45





根据文档,必须公开继承“shared_from_this”成员函数的accessiblity。

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

shared_from_this:

返回一个shared_ptr,它共享* this的所有权 (公共会员功能)


0
2017-10-08 20:42



您的答案实际上并没有为问题添加任何新内容:shared_from_this必须公开继承。有趣的部分是为什么会有这样的限制。 - Alexey Guseynov
应该继承“enable_shared_from_this”.....而不是“shared_from_this”。而“shared_from_this”是基类中的一个函数,它返回“* this”指针。请注意,类名和函数名之间还有一个单词“enable_”。 - Naidu
你知道的正确...... Base类的受保护和公共成员成为Derived类的私有成员。因此,其他类不能通过Derived类对象访问Base类的成员,因为它们在Derived类中是私有的。 - Naidu
在我的回答中仔细阅读“for accessiblity”这个词。它将解释我的答案。 - Naidu


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

auto 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可以是enable_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。


0
2018-05-30 01:14