本文介绍了Qt插槽和C ++ 11 Lambda的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个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
}

此操作工作正常,我得到了调用者对象,并且可以按预期使用它。然后,我尝试使用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
});

但是呼叫者始终为空,因此 Q_ASSERT 触发器。我该如何使用lambda来获取发件人?

But caller is always null and thus the Q_ASSERT triggers. How can I use lambdas to get the sender?

推荐答案

简单的答案是:不能。或者,更确切地说,您不想(或不需要!)使用 sender()。只需捕获并使用操作

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 作为对象上下文的规范for functor确保如果操作或 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的仿函数的上下文变量时,必须满足以下条件: / code>,以避免使用悬空的指针/引用:

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:


  1. 指向源和如上所述, connect 的目标对象可以按值捕获。可以保证,如果调用函子,则连接的两端都存在。

  1. 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 来说,空线程永远不会成为问题-如果没有线程,就不会调用函子。 las,我们无法保证 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用于引用对象:

  • Ditto for references to objects:

    static D d;
    connect(..., [&d]{});
    


  • 不是从 QObject派生的不可复制对象/ code>应该通过它们的共享指针按值捕获。

  • 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 可以捕获同一线程;

  • QObjects 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 线程必须由共享指针或弱指针捕获。它们的父对象必须在销毁之前取消设置,否则您将被双重删除:

  • QObjects 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

    08-20 06:37
    查看更多