問題描述
我有一個 QAction 項,我初始化如下:
I have a QAction item that I initialize like follows:
QAction* action = foo->addAction(tr("Some Action"));
connect(action, SIGNAL(triggered()), this, SLOT(onSomeAction()));
然后 onSomeAction 看起來像:
And then onSomeAction looks something like:
void MyClass::onSomeAction()
{
QAction* caller = qobject_cast<QAction*>(sender());
Q_ASSERT(caller != nullptr);
// do some stuff with caller
}
這很好用,我取回了 caller
對象,并且可以按預期使用它.然后我嘗試使用 C++11 的方式來連接對象,如下所示:
This works fine, I get the caller
object back and I'm able to use it as expected. Then I try the C++11 way to connect the object like such:
connect(action, &QAction::triggered, [this]()
{
QAction* caller = qobject_cast<QAction*>(sender());
Q_ASSERT(caller != nullptr);
// do some stuff with caller
});
但是 caller
始終為 null,因此 Q_ASSERT
會觸發.如何使用 lambdas 來獲取發件人?
But caller
is always null and thus the Q_ASSERT
triggers. How can I use lambdas to get the sender?
推薦答案
簡單的答案是:你不能.或者,更確切地說,您不想(或不需要!)使用 sender()
.只需捕獲并使用 action
.
The simple answer is: you can't. Or, rather, you don't want (or need!) to use sender()
. Simply capture and use action
.
// Important!
// vvvv
connect(action, &QAction::triggered, this, [action, this]() {
// use action as you wish
...
});
this
作為函子的對象上下文的規范確保函子不會被調用,如果動作或 this
(一個 QObject代碼>) 不復存在.否則,函子會嘗試引用懸空指針.
The specification of this
as the object context for the functor ensures that the functor will not get invoked if either the action or this
(a QObject
) cease to exist. Otherwise, the functor would try to reference dangling pointers.
通常,在為傳遞給 connect
的函子捕獲上下文變量時,必須滿足以下條件,以避免使用懸空指針/引用:
In general, the following must hold when capturing context variables for a functor passed to connect
, in order to avoid the use of dangling pointers/references:
connect
的源和目標對象的指針可以通過值來捕獲,如上.保證如果調用函子,連接的兩端都存在.
The pointers to the source and target objects of
connect
can be captured by value, as above. It is guaranteed that if the functor is invoked, both ends of the connection exist.
connect(a, &A::foo, b, [a, b]{});
a
和 b
在不同線程中的情況需要特別注意.不能保證一旦進入函子,某個線程就不會刪除任何一個對象.
Scenarios where a
and b
are in different threads require special attention. It can not be guaranteed that once the functor is entered, some thread will not delete either object.
一個對象只在它的thread()
中被破壞,或者在任何線程中,如果thread() == nullptr
,這是慣用的.由于線程的事件循環調用函子,因此對于 b
而言,空線程永遠不會成為問題——沒有線程,函子將不會被調用.唉,b
的線程中 a
的生命周期無法保證.因此,通過值捕獲操作的必要狀態會更安全,這樣 a
的生命周期就不是問題.
It is idiomatic that an object is only destructed in its thread()
, or in any thread if thread() == nullptr
. Since a thread's event loop invokes the functor, the null thread is never a problem for b
- without a thread the functor won't be invoked. Alas, there's no guarantee about the lifetime of a
in b
's thread. It is thus safer to capture the necessary state of the action by value instead, so that a
's lifetime is not a concern.
// 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(); });
如果您絕對確定指向其他對象的對象的生命周期與連接的生命周期重疊,則可以通過值捕獲指向其他對象的原始指針.
Raw pointers to other objects can be captured by value if you're absolutely sure that the lifetime of the objects they point to overlaps the lifetime of the connection.
static C c;
auto p = &c;
connect(..., [p]{});
對象引用同上:
Ditto for references to objects:
static D d;
connect(..., [&d]{});
非從 QObject
派生的不可復制對象應通過它們的共享指針按值捕獲.
Non-copyable objects that don't derive from QObject
should be captured through their shared pointers by value.
std::shared_ptr<E> e { new E };
QSharedPointer<F> f { new F; }
connect(..., [e,f]{});
QObject
可以被QPointer
捕獲;在函數中使用之前必須檢查它的值.
QObject
s living in the same thread can be captured by a QPointer
; its value must be checked prior to use in the functor.
QPointer<QObject> g { this->parent(); }
connect(..., [g]{ if (g) ... });
QObject
存在于其他線程中,必須被共享指針或弱指針捕獲.他們的父級必須在銷毀之前取消設置,否則您將進行雙重刪除:
QObject
s living in other threads must be captured by a shared pointer or a weak pointer. Their parent must be unset prior to their destruction, otherwise you'll have double deletes:
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) { ... }
});
這篇關于Qt 插槽和 C++11 lambda的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!