如何在 C++11 lambda 中跟踪对象生存期
How can I track object lifetime in C++11 lambda?
有时,我们对捕获对象状态的lambda的生命周期一无所知(例如,从对象返回它,将其注册为回调而无法取消订阅等)。如何确保 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;
}
该程序打印"WRONG"而不是"OK"(http://ideone.com/Srp7RC)只是巧合(似乎它只是为第二个Foo
对象重复使用相同的内存)。无论如何,这是一个错误的程序。当我们执行 f
时,首先Foo
对象已经死了。
延长对象生存期
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
通常,这不是一个好的解决方案,因为对象生存期在这里以非常隐式的方式延长。这是在对象之间生成循环引用的非常简单的方法。
跟踪对象生存期
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::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
在没有共享指针的情况下跟踪对象生存期
正如 hvd 所指出的,我们不能总是确定一个对象是由 shared_ptr
管理的。在这种情况下,我建议使用以下lifetime_tracker
。它是独立的,不会影响您管理对象生存期的方式。
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;
};
使用示例
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;
};
您可以确定对象由shared_ptr
管理时,Stas 的答案很好,但这并不总是可能的。不过,您始终可以做的是跟踪对象的生存期,并在 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};
};
在构造函数中,共享指针monitor
不是显式初始化的,而是通过其初始化器设置为指向this
,并且在指针的生存期到期后不执行任何操作。这个想法不是让shared_ptr
负责释放对象,这个想法只是让shared_ptr
传递有关对象生命周期的信息。
在创建 lambda 之前,您可以构造一个跟踪关联shared_ptr
的weak_ptr
。如果对象已被销毁,则其monitor
成员也必然已被销毁,并且通过expired()
函数可见。
- C++:跟踪类对象
- MATLAB:跟踪imufilter对象中的状态变化
- mingw32-make 使用"MinGW Makefiles"生成器跟踪 CMAKE 无法将可执行文件链接到对象库
- 内存分配究竟是如何发生的,Java和C如何交互以跟踪同一对象?
- 如何在类中声明一个变量,该变量将跟踪已创建 c++ 的对象计数
- 从实时摄影机馈送而不是图像中按形状跟踪对象
- 如何清理本地引用中跟踪C++对象<External>?
- 如何确定我跟踪的对象球是否越过我绘制的线
- 跟踪对象类型
- 如何跟踪安排以后删除的对象
- GNU GCC会跟踪以前使用箭头操作员对的对象
- 使用OPENCV跟踪对象的多相机校准
- Tcl:解释器创建跟踪对象的副本,当它被更改时
- 如何在 C++11 lambda 中跟踪对象生存期
- 在 Haar 级联 opencv 之后跟踪对象
- 复制构造函数并通过跟踪对象移动语义
- 在QTreeView/QStandardItem中跟踪对象的正确方法是什么?
- 如何跟踪c++对象
- 跟踪对象和引用
- 尽管初始大小固定,但用于跟踪对象的边界框/ROI 的大小仍在不断增加