\`enable_shared_from_this\` 有什么用处?

新手上路,请多包涵

我在阅读 Boost.Asio 示例和阅读文档后遇到了 enable_shared_from_this 我仍然不知道应该如何正确使用它。有人可以给我一个例子和解释什么时候使用这个类是有意义的。

原文由 fido 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 401
2 个回答

它使您能够获得有效的 shared_ptr 实例到 this ,而您所拥有的只是 this 。没有它,您将无法获得 shared_ptrthis ,除非您已经有一个会员。此示例来自 enable_shared_from_this 的 boost 文档

 class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

方法 f() 返回一个有效的 shared_ptr ,即使它没有成员实例。请注意,您不能简单地这样做:

 class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_ptr<Y>(this);
    }
}

此返回的共享指针将具有与“正确”的引用计数不同的引用计数,并且当对象被删除时,其中一个最终将丢失并持有一个悬空引用。

enable_shared_from_this 已成为 C++ 11 标准的一部分。您也可以从那里以及从 boost 中获取它。

原文由 1800 INFORMATION 发布,翻译遵循 CC BY-SA 4.0 许可协议

在一种特殊情况下,我发现 enable_shared_from_this 非常有用:使用异步回调时的线程安全。

想象类 Client 有一个类型为 AsynchronousPeriodicTimer 的成员:

 struct AsynchronousPeriodicTimer
{
    // call this periodically on some thread...
    void SetCallback(std::function<void(void)> callback);
    void ClearCallback(); // clears the callback
}

struct Client
{
    Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
        : _timer(timer)

    {
        _timer->SetCallback(
            [this]
            ()
            {
                assert(this); // what if 'this' is already dead because ~Client() has been called?
                std::cout << ++_counter << '\n';
            }
            );
    }
    ~Client()
    {
        // clearing the callback is not in sync with the timer, and can actually occur while the callback code is running
        _timer->ClearCallback();
    }
    int _counter = 0;
    std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}

int main()
{
    auto timer = std::make_shared<AsynchronousPeriodicTimer>();
    {
        auto client = std::make_shared<Client>(timer);
        // .. some code
        // client dies here, there is a race between the client callback and the client destructor
    }
}

客户端类为周期性计时器订阅回调函数。一旦客户端对象超出范围,客户端的回调和客户端的析构函数之间就会出现竞争条件。可以使用悬空指针调用回调!

解决方案:使用 enable_shared_from_this 在回调调用期间延长对象生命周期。

 struct Client : std::enable_shared_from_this<Client>
{
Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
    : _timer(timer)

    {

    }

    void Init()
    {
        auto captured_self = weak_from_this(); // weak_ptr to avoid cyclic references with shared_ptr

        _timer->SetCallback(
        [captured_self]
        ()
        {
            if (auto self = captured_self.lock())
            {
                // 'this' is guaranteed to be non-nullptr. we managed to promote captured_self to a shared_ptr
                std::cout << ++self->_counter << '\n';
            }

        }
        );
    }
    ~Client()
    {
        // the destructor cannot be called while the callback is running. shared_ptr guarantees this
        _timer->ClearCallback();

    }
    int _counter = 0;
    std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}

enable_shared_from_this 的机制,结合 std::shared_ptr 引用计数固有的线程安全,使我们能够保证 Client 对象不能被破坏而回调代码被破坏访问其内部成员。

注意 Init 方法与构造函数是分开的,因为 enable_shared_from_this 的初始化过程直到构造函数退出才完成。因此,额外的方法。从构造函数中订阅异步回调通常是不安全的,因为回调可能会访问未初始化的字段。

原文由 Elad Maimoni 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题