参考链接资料汇总

如果你是搜索到这里的,请注意这个问题描述的场景

  • 对象自身的回调捕获了自己的值,自己造成了循环

出事的代码是这样的

class my_class {
// ...
public:
  typedef std::function<void()> callback;
  void on_complete(callback cb) { complete_callback = cb; }
private:
  callback complete_callback;
// ...
};
 
// ...
  std::shared_ptr<my_class> obj = std::make_shared<my_class>();
  obj->on_complete([obj]() {
    obj->clean_something_up();
  });
  executor->submit(obj);
// ...

注意,lambda默认是const,不改变值 SO

lambda的本质就是传值的一个指针,如果这里用std::function保存,会导致这个捕获的指针泄漏,导致这个指针永远不释放

解决方案1,weak_ptr,或者weak_from_this


class my_class {
// ...
public:
  typedef std::function<void()> callback;
  void on_complete(callback cb) { complete_callback = cb; }
private:
  callback complete_callback;
// ...
};
 
// ...
  std::shared_ptr<my_class> obj = std::make_shared<my_class>();
  std::weak_ptr<my_class> weak_obj(obj);
 
  obj->on_complete([weak_obj]() {
    auto obj = weak_obj.lock();
    if (obj) {
      obj->clean_something_up();
    }
  });
  executor->submit(obj);

解决方案2 ,lambda加上 mutable,见这个SO

参考链接2给出了一个比较经典的循环

void capture_by_value(uvw::Loop &loop) {
    auto conn = loop.resource<uvw::TcpHandle>();
    auto timer = loop.resource<uvw::TimerHandle>();

    // OK: capture uses [=]
    conn->on<uvw::CloseEvent>([=](const auto &, auto &) {
        timer->close();
    });

    // Now timer has a callback to conn, and vice versa...
    timer->on<uvw::CloseEvent>([=](const auto &, auto &) {
        conn->close();
    });
};

这里conn和timer循环了,如何规避?weak_from_this(前提,继承std::enable_shared_from_this.)

void capture_weak_by_value(uvw::Loop &loop) {
    auto conn = loop.resource<uvw::TcpHandle>();
    // Create a std::weak_ptr to the connection. weak_from_this() is new
    // in C++17, and is enabled on all classes that inherit from
    // std::enable_shared_from_this.
    auto w_conn = conn->weak_from_this();

    auto timer = loop.resource<uvw::TimerHandle>();
    auto w_timer = timer->weak_from_this();  // as above

    // OK, uses weak_ptr
    conn->on<uvw::CloseEvent>([=](const auto &, auto &) {
        if (auto t = w_timer.lock()) t->close();
    });

    // OK, uses weak_ptr
    timer->on<uvw::CloseEvent>([=](const auto &, auto &) {
        if (auto c = w_conn.lock()) c->close();
    });
});

ref

  1. http://web.archive.org/web/20180324083405/http://https://floating.io/2017/07/lambda-shared_ptr-memory-leak/ 原网址挂了
  2. https://eklitzke.org/notes-on-std-shared-ptr-and-std-weak-ptr 介绍weak_from_this