我有一个QAction项目,我初始化如下:
QAction* action = foo->addAction(tr("Some Action"));
connect(action, SIGNAL(triggered()), this, SLOT(onSomeAction()));
Run Code Online (Sandbox Code Playgroud)
然后onSomeAction看起来像:
void MyClass::onSomeAction()
{
QAction* caller = qobject_cast<QAction*>(sender());
Q_ASSERT(caller != nullptr);
// do some stuff with caller
}
Run Code Online (Sandbox Code Playgroud)
这工作正常,我得到了caller
对象,我可以按预期使用它.然后我尝试使用C++ 11方式连接对象,如下所示:
connect(action, &QAction::triggered, [this]()
{
QAction* caller = qobject_cast<QAction*>(sender());
Q_ASSERT(caller != nullptr);
// do some stuff with caller
});
Run Code Online (Sandbox Code Playgroud)
但是caller
始终为null,因此Q_ASSERT
触发器.我怎样才能使用lambdas来获取发件人?
Rei*_*ica 74
简单的答案是:你做不到.或者,您不希望(或需要!)使用sender()
.只需捕获和使用action
.
// Important!
// vvvv
connect(action, &QAction::triggered, this, [action, this]() {
// use action as you wish
...
});
Run Code Online (Sandbox Code Playgroud)
this
作为仿函数的对象上下文的规范确保如果动作或this
(a QObject
)不再存在,则不会调用仿函数.否则,仿函数会尝试引用悬空指针.
通常,在捕获传递给的仿函数的上下文变量时必须满足以下条件connect
,以避免使用悬空指针/引用:
如上所述,connect
可以通过值捕获指向源和目标对象的指针.保证如果调用仿函数,则存在连接的两端.
connect(a, &A::foo, b, [a, b]{});
Run Code Online (Sandbox Code Playgroud)
不同线程中的场景a
和场景b
需要特别注意.无法保证一旦输入仿函数,某些线程就不会删除任何一个对象.
如果对象仅在其中thread()
或在任何线程中被破坏,这是惯用的thread() == nullptr
.由于线程的事件循环调用了仿函数,因此空线程永远不会成为问题b
- 如果没有线程,则不会调用仿函数.唉,不能保证a
in b
thread 的生命周期.因此,通过价值来捕捉行动的必要状态是更安全的,因此a
寿命不是问题.
// SAFE
auto aName = a->objectName();
connect(a, &A::foo, b, [aName, b]{ qDebug() << aName; });
// UNSAFE
connect(a, &A::foo, b, [a,b]{ qDebug() << a->objectName(); });
Run Code Online (Sandbox Code Playgroud)如果您完全确定它们指向的对象的生命周期与连接的生命周期重叠,则可以通过值捕获到其他对象的原始指针.
static C c;
auto p = &c;
connect(..., [p]{});
Run Code Online (Sandbox Code Playgroud)同上对象的引用:
static D d;
connect(..., [&d]{});
Run Code Online (Sandbox Code Playgroud)不派生的非可复制对象QObject
应通过其共享指针按值捕获.
std::shared_ptr<E> e { new E };
QSharedPointer<F> f { new F; }
connect(..., [e,f]{});
Run Code Online (Sandbox Code Playgroud)QObject
生活在同一个线程中的人可以被一个人捕获QPointer
; 在使用仿函数之前必须检查其值.
QPointer<QObject> g { this->parent(); }
connect(..., [g]{ if (g) ... });
Run Code Online (Sandbox Code Playgroud)QObject
生活在其他线程中的s必须由共享指针或弱指针捕获.他们的父母必须在他们的破坏之前解开,否则你将有双重删除:
class I : public QObject {
...
~I() { setParent(nullptr); }
};
std::shared_ptr<I> i { new I };
connect(..., [i]{ ... });
std::weak_ptr<I> j { i };
connect(..., [j]{
auto jp = j.lock();
if (jp) { ... }
});
Run Code Online (Sandbox Code Playgroud)MiB*_*der 12
使用lambdas作为插槽非常简单(例如,来自QSpinbox的事件):
connect(spinboxObject, &QSpinBox::editingFinished, this, [this]() {<do something>});
Run Code Online (Sandbox Code Playgroud)
但这只有在信号没有过载时才有效(这意味着有几个信号具有相同的名称但参数不同).
connect(spinboxObject, &QSpinBox::valueChange, this, [this]() {<do something>});
Run Code Online (Sandbox Code Playgroud)
给出了编译错误,因为存在两个重载信号:valueChanged(int)和valueChanged(const QString&)因此有必要确定应该使用哪个版本:
connect(spinboxObject, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this](int newValue){ });
Run Code Online (Sandbox Code Playgroud)
更短(或更好的可读性)是使用QOverload:
connect(spinboxObject, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int newValue) { });
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
32170 次 |
最近记录: |