Qt 插槽和 C++11 lambda [英] Qt Slots and C++11 lambda

查看:44
本文介绍了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
}

这很好用,我取回了 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:

  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]{});

ab 在不同线程中的情况需要特别注意.不能保证一旦进入函子,某个线程就不会删除任何一个对象.

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捕获;在函数中使用之前必须检查它的值.

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

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆