[英]How can I track object lifetime in C++11 lambda?
Sometimes, we know nothing about lifetime of lambda that captures an object state (eg return it from object, register it as a callback without ability to unsubscribe etc.). 有时,我们对捕获对象状态的lambda的生命周期一无所知(例如,将其从对象返回,将其注册为回调而无需取消订阅等)。 How to make sure that the lambda won't access already destroyed object on invocation? 如何确保lambda在调用时不会访问已销毁的对象?
#include <iostream>
#include <memory>
#include <string>
class Foo {
public:
Foo(const std::string& i_name) : name(i_name) {}
std::function<void()> GetPrinter() {
return [this]() {
std::cout << name << std::endl;
};
}
std::string name;
};
int main() {
std::function<void()> f;
{
auto foo = std::make_shared<Foo>("OK");
f = foo->GetPrinter();
}
auto foo = std::make_shared<Foo>("WRONG");
f();
return 0;
}
This program prints "WRONG" instead of "OK" ( http://ideone.com/Srp7RC ) just by coincidence (it seems it just reused the same memory for the second Foo
object). 这个程序只是巧合地打印出“WRONG”而不是“OK”( http://ideone.com/Srp7RC )(似乎它只是为第二个Foo
对象重用了相同的内存)。 Anyway, this is a wrong program. 无论如何,这是一个错误的计划。 First Foo
object is already dead when we execute f
. 当我们执行f
时,第一个Foo
对象已经死了。
Lambdas can capture shared pointers to this
, so an object won't die while at least one lambda exists. Lambdas可以捕获this
共享指针,因此当至少存在一个lambda时,对象不会死亡。
class Foo : public std::enable_shared_from_this<Foo> {
public:
Foo(const std::string& i_name) : name(i_name) {}
std::function<void()> GetPrinter() {
std::shared_ptr<Foo> that = shared_from_this();
return [that]() {
std::cout << that->name << std::endl;
};
}
std::string name;
};
http://ideone.com/Ucm2p8 http://ideone.com/Ucm2p8
Usually, it is not a good solution, as object lifetime is extended in very implicit manner here. 通常,这不是一个好的解决方案,因为对象生命周期在这里以非常隐式的方式扩展。 It is very easy way of producing circular references between objects. 这是在对象之间生成循环引用的非常简单的方法。
Lambdas can track captured object lifetime and use the object only if it is still alive. Lambdas可以跟踪捕获的对象生命周期,并仅在对象仍然存活时使用该对象。
class Foo : public std::enable_shared_from_this<Foo> {
public:
Foo(const std::string& i_name) : name(i_name) {}
std::function<void()> GetPrinter() {
std::weak_ptr<Foo> weak_this = shared_from_this();
return [weak_this]() {
auto that = weak_this.lock();
if (!that) {
std::cout << "The object is already dead" << std::endl;
return;
}
std::cout << that->name << std::endl;
};
}
std::string name;
};
http://ideone.com/Wi6O11 http://ideone.com/Wi6O11
As hvd noted , we can't always be sure that an object is managed by shared_ptr
. 正如hvd 所说 ,我们不能总是确定一个对象是由shared_ptr
管理的。 In such case, I would suggest using the following lifetime_tracker
. 在这种情况下,我建议使用以下lifetime_tracker
。 It is self-contained and doesn't affect the way you manage object lifetime. 它是自包含的,不会影响您管理对象生存期的方式。
struct lifetime_tracker
{
private:
struct shared_state
{
std::uint32_t count : 31;
std::uint32_t dead : 1;
};
public:
struct monitor
{
monitor() : state(nullptr) {}
monitor(shared_state *i_state) : state(i_state) {
if (state)
++state->count;
}
monitor(const monitor& t) : state(t.state) {
if (state)
++state->count;
}
monitor& operator=(monitor t) {
std::swap(state, t.state);
return *this;
}
~monitor() {
if (state) {
--state->count;
if (state->count == 0 && state->dead)
delete state;
}
}
bool alive() const {
return state && !state->dead;
}
private:
shared_state *state;
};
public:
lifetime_tracker() : state(new shared_state()) {}
lifetime_tracker(const lifetime_tracker&) : state(new shared_state()) {}
lifetime_tracker& operator=(const lifetime_tracker& t) { return *this; }
~lifetime_tracker() {
if (state->count == 0)
delete state;
else
state->dead = 1;
}
monitor get_monitor() const {
return monitor(state);
}
private:
shared_state *state;
};
Example of usage 用法示例
class Foo {
public:
Foo(const std::string& i_name) : name(i_name) {}
std::function<void()> GetPrinter() {
auto monitor = tracker.get_monitor();
return [this, monitor]() {
if (!monitor.alive()) {
std::cout << "The object is already dead" << std::endl;
return;
}
std::cout << this->name << std::endl;
};
}
private:
lifetime_tracker tracker;
std::string name;
};
Stas's answer is good when you can be certain that objects are managed by a shared_ptr
, but that isn't always possible. 当您可以确定对象由shared_ptr
管理时,Stas的答案很好,但这并不总是可行的。 What you can always do, though, is track the lifetime of objects, and add an assertion in your lambda. 但是,你总能做的是跟踪对象的生命周期,并在lambda中添加一个断言。
void ignore(void *) { }
class Foo {
public:
Foo(const std::string& i_name) : name(i_name) {}
Foo(const Foo& other) : name(other.name) {}
Foo(Foo&& other) : name(std::move(other.name)) {}
Foo& operator=(Foo other) { swap(*this, other); return *this; }
friend void swap(Foo& a, Foo& b) { using std::swap; swap(a.name, b.name); }
std::function<void()> GetPrinter() {
std::weak_ptr<void> monitor = this->monitor;
return [=]() {
assert (!monitor.expired());
std::cout << name << std::endl;
};
}
std::string name;
private:
std::shared_ptr<void> monitor{this, ignore};
};
In the constructor, the shared pointer monitor
is not explicitly initialised, but set up through its initialiser to point to this
, and to do nothing once the pointer's lifetime expires. 在构造函数中,共享指针monitor
未显式初始化,而是通过其初始化器设置为指向this
,并且在指针的生存期到期后不执行任何操作。 The idea isn't to make shared_ptr
responsible for freeing the object, the idea is only to let shared_ptr
pass along information on the object's lifetime. 我们的想法不是让shared_ptr
负责释放对象,这个想法只是让shared_ptr
传递有关对象生命周期的信息。
Prior to the creation of your lambda, you can construct a weak_ptr
which tracks the associated shared_ptr
. 在创建lambda之前,您可以构造一个跟踪关联的shared_ptr
的weak_ptr
。 If the object has been destroyed, then its monitor
member will necessarily also have been destroyed, and that becomes visible through the expired()
function. 如果对象已被销毁,那么它的monitor
成员也必然已被销毁,并且通过expired()
函数可以看到它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.