【问题标题】:Alternative to capture this in lambda [C++]在 lambda [C++] 中捕获这个的替代方法
【发布时间】:2017-04-16 14:51:42
【问题描述】:

我知道我是否必须在 lambda 中调用成员函数以进行回调,我在 lambda 中捕获了它并且它运行良好。 但是我最近看到了一些崩溃,似乎在 this 指向的对象已经被破坏之后访问了成员函数。 简而言之,关闭时对象被销毁,但指针在 lambda 中传递,被访问并导致崩溃。

所以,我试图了解社区在这种情况下通常会做什么。我找不到太多,但我认为 shared_ptr 可能是一个选项。

任何建议/线索将不胜感激,以帮助我理解和实施替代方案。

【问题讨论】:

  • 能发个sn-p代码吗?
  • 您可以从std::enable_shared_from_this 继承,使用shared_from_this() 获取共享指针并捕获std::shared_ptr,如果您想确保对象还活着,或者std::weak_ptr,如果你想在做任何操作之前检查它是否还活着。
  • 我将统一@TheEyesightDim。我们可以得到minimal reproducible example 吗?
  • [self = *this]() { self.f(); }?
  • @KerrekSB 在这种情况下,您必须保证该类具有正确的构造函数。并非总是如此。当然,您也不一定可以将其修改为从std::enable_shared_from_this 继承。

标签: c++ c++11 lambda this shared-ptr


【解决方案1】:

在 C++ 中,您负责跟踪对象的生命周期。

这意味着您必须跟踪持有指向其他事物的指针和引用的事物的生命周期,并确保它们不会像那些事物一样长寿。

你的任务失败了。您传递的 lambdas 捕获指向周围对象的指针,就好像它们在糖果中一样,而不是直接进入对象的内部。

通过散布共享指针来解决生命周期问题通常是一个坏主意。使对象的生命周期更加模糊可能会减少立即崩溃的事件,但是对象生命周期的模糊球不会使您的程序工作。朦胧的球要么扩大到包含你的整个程序,现在永远无法真正关闭,要么它重新回到自身并自我延续,泄漏资源。

共享指针可以用在你有一个定义的生命周期关系的狭隘情况下,这种关系最好被建模为共享所有权。这与“我有对象在它们的指针之前离开,所以我应该尝试共享指针!”完全不同。你有一个对象生命周期问题。你试试共享指针。现在你有两个问题:原始对象生命周期问题和共享指针问题。

回调是您需要严格的生命周期规则的一个例子。多久回电?你什么时候停下来?您回收资源的紧迫程度如何?你如何取消注册回调?等等。

我编写了使用共享指针和弱指针的回调系统。它们并不完美。这是我在谷歌找到的一个:broadcaster。听众存储令牌以说“继续与我交谈”,当他们离开时,广播公司将停止喋喋不休。

【讨论】:

  • 锁定的weak_ptr仍然是消除订阅者离开而未完成的回调潜伏在消息队列中的交叉情况的最可靠和最简单的方法。
  • @RichardHodges 请注意,broadcaster<Args...> 会这样做,其中“队列”是广播目标的向量。
【解决方案2】:

这是我用于处理订阅的模式。使用锁定的weak_ptr 消除了交叉案例的风险。

#include <memory>
#include <chrono>
#include <thread>
#include <mutex>

using namespace std::literals;

// some external service that will send us events in a callback.
// normally of course we'd have some means to turn these off too.
// However for this demo we'll ignore that for now
void subscribe_for_events(std::function<void()> f);

struct might_go_away : std::enable_shared_from_this<might_go_away>
{
    static std::shared_ptr<might_go_away> create() {
        auto p = std::make_shared<might_go_away>();
        p->start();
    }

    might_go_away() {}

private: 

    using mutex_type = std::mutex;
    using lock_type = std::unique_lock<mutex_type>;

    // handy helper to deliver a weak pointer to ourselves
    auto weak_self() { return std::weak_ptr<might_go_away>(shared_from_this()); }

    // do startup things here, like subscribing to other services etc
    void start() {
        subscribe_for_events([this, weak = this->weak_self()]
        {
            // don't touch 'this' until we have successfully locked the weak ptr
            if (auto self = weak.lock()) {
                // we know we're alive. the variable 'self' will hold the strong count > 0
                // this is a good place to take locks
                handle_event(lock_type(mutex_));
            }
        });
    }

    void handle_event(lock_type) {
        // do things when notified by some event source.
        // we are safe here. `this` will not go away and we own the mutex 
        // we will release the lock when this function exits through RAII.
        // PLUS, because the lock was moved in, we own it. We can release it early if we wish.
    }

    mutex_type mutex_;
};

当然,在生产代码中,shared_ptr 将包装在面向消费者的句柄类中。

我们在可能的情况下更喜欢值语义。

struct active_thing
{
    using implementation_class = might_go_away;
    using implementation_type = std::shared_ptr<implementation_class>;

    active_thing() : impl_(implementation_class::create()) {}

    // methods here

private:
    implementation_type impl_;
};


int main()
{
  {
    auto a = active_thing();
    std::this_thread::sleep_for(5s);
  }
}

【讨论】:

  • weak_self ... 等待即将到来的weak_from_this ... :-)
  • 可能他们接受了迟早承诺的稍后部分。 :-)
  • @skypjack 所以我们将在 2020 年获得延续期货和网络,然后在 2026 年实现基本的 http 客户端。图形必须等到 2032 年......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
  • 2019-05-11
相关资源
最近更新 更多