gpt4 book ai didi

c++ - 在用作槽的 lambda 函数中捕获的堆栈分配变量的生命周期是多少?

转载 作者:太空狗 更新时间:2023-10-29 21:13:03 25 4
gpt4 key购买 nike

我需要帮助了解 lambda 函数的工作方式,以防止在使用它们时发生内存泄漏。更具体地说,我想知道在以下情况下 foo 何时会被销毁:

void MainWindow::onButtonClicked()
{
QTimer *t(new QTimer(this));
bool foo = false;

t->setSingleShot(true);
t->setInterval(1000);
t->start();

connect(t, &QTimer::timeout, [=](){
delete t;
qDebug() << foo;
});
}

使用[&]的情况呢?

最佳答案

一个求值的 lambda 表达式是一个仿函数实例。仿函数是一个带有 operator() 的对象。捕获的变量是该仿函数对象的成员。 它们的生命周期不会根据它们的类型而改变。因此,无论您捕获引用还是值,它们的生命周期都是相同的。 您的工作是确保引用有效 - 即它们引用的对象没有被破坏。

仿函数的生命周期与连接的生命周期相同。连接以及仿函数将持续到:

  1. QObject::disconnect()QObject::connect() 的返回值上被调用,或者

  2. QObject 的生命结束,它的析构函数被调用。

考虑到上述情况,局部变量引用捕获的唯一有效用途是局部变量比连接更长的时间。一些有效的例子是:

void test1() {
int a = 5;
QObject b;
QObject:connect(&b, &QObject::destroyed, [&a]{ qDebug() << a; });
// a outlives the connection - per C++ semantics `b` is destroyed before `a`
}

或者:

int main(int argc, char **argv) {
QObject * focusObject = {};
QApplication app(argc, argv);
QObject * connect(&app, &QGuiApplication::focusObjectChanged,
[&](QObject * obj){ focusObject = obj; });
//...
return app.exec(); // focusObject outlives the connection too
}

您问题中的代码不必要地复杂。无需手动管理此类计时器:

void MainWindow::onButtonClicked() {
bool foo = {};
QTimer::singleShot(1000, this, [this, foo]{ qDebug() << foo; });
}

这里的重要部分是提供对象上下文 (this) 作为 singleShot 的第二个参数。这确保了 this 必须比仿函数活得更久。相反,仿函数将在 this 被销毁之前被销毁。

假设您真的想实例化一个新的 transient 计时器,删除连接到此类信号的槽中信号的源对象是未定义的行为。您必须将删除推迟到事件循环:

void MainWindow::onButtonClicked()
{
auto t = new QTimer(this);
bool foo = {};

t->setSingleShot(true);
t->setInterval(1000);
t->start();

connect(t, &QTimer::timeout, [=](){
qDebug() << foo;
t->deleteLater();
});
}

tfoo 都被复制到仿函数对象中。 lambda 表达式是一种符号速记 - 您可以自己明确地编写它:

class $OpaqueType {
QTimer * const t;
bool const foo;
public:
$OpaqueType(QTimer * t, bool foo) :
t(t), foo(foo) {}
void operator()() {
qDebug() << foo;
t->deleteLater();
}
};

void MainWindow::onButtonClicked() {
//...
connect(t, &QTimer::timeout, $OpaqueType(t, foo));
}

由于 lambda 实例只是一个对象,如果多个信号需要连接到同一个 lambda,您当然可以将它分配给一个变量并消除代码重复:

auto f = [&]{ /* code */ };
connect(o, &Class::signal1, this, f);
connect(p, &Class::signal2, this, f);

lambda 的类型是唯一的、不可言说的,也称为不透明类型。你不能从字面上提及它——语言中没有这样做的机制。您只能通过 decltype 引用它。这里的 decltypehe who shall not be named 中的 he。 C++ 的人只是在哈利波特的笑话中工作,不管他们是否有意。否则我不会相信。

关于c++ - 在用作槽的 lambda 函数中捕获的堆栈分配变量的生命周期是多少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45644334/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com