QObject :: connect函数中的QObject *上下文

时间:2015-01-14 21:36:56

标签: c++ qt c++11

我读了documentation的QObject :: connect(对于Qt 5.4),但我有一个关于重载的问题

QMetaObject::Connection QObject::connect(const QObject * sender, PointerToMemberFunction signal, const QObject * context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)

context参数究竟是什么?它的目的是什么?它可以用于在线程中的本地事件循环中构建连接吗?

有人可以举例说明如何/何时使用此重载(当上下文不是this时)?

1 个答案:

答案 0 :(得分:21)

上下文对象用于两种情况。

自动断开连接

让我们先退后一步,问自己: Qt什么时候断开连接?

通常使用connect(sender, signal, receiver, slot)连接,有三种可能性:

  1. 有人明确致电disconnect;
  2. 删除sender
  3. 删除receiver时。
  4. 特别是在#2和#3的情况下,Qt的行为是有意义的(实际上,必须表现那样,否则你会有资源泄漏和/或崩溃)。


    现在:使用connect重载时使用仿函数,Qt何时断开连接?

    请注意,如果没有context参数,则只涉及一个QObject:发件人。因此答案是:

    1. 有人明确致电disconnect;
    2. 删除sender时。
    3. 当然,这里没有接收器对象!因此,只有发件人才能自动控制连接的生命周期。

      现在,问题是仿函数可能会捕获一些可能变为无效的额外状态,在这种情况下,连接会自动断开。典型的案例是lambdas:

      connect(sender, &Sender::signal, 
              [&object1, &object2](Param p) 
              { 
                  use(object1, object2, p);
              }
      );
      

      如果object1object2被删除会怎样?连接仍然是活动的,因此发出信号仍将调用lambda,而lambda将访问被破坏的对象。这有点不好......


      出于这个原因,当涉及到仿函数时,引入了一个connect重载上下文对象。使用该过载建立的连接也将自动断开

        删除context对象时
      1. 当你说很多次你会在那里看到相同的" main"时,你可能是对的。例如,在仿函数中使用的对象

        connect(button, 
                &QPushButton::clicked,
                otherWidget, 
                [otherWidget]() 
                { 
                    otherWidget->doThis(); otherWidget->doThat(); 
                }
        );
        

        这只是Qt中的模式 - 在为子对象设置连接时,通常将它们连接到this对象上的插槽,因此this可能是最常见的上下文。但是,一般来说,您最终可能会遇到类似

        的内容
        // manages the lifetime of the resources; they will never outlive this object
        struct ResourceManager : QObject 
        {
            Resource res1; // non-QObjects
            OtherResource res2;
        };
        
        ResourceManager manager;    
        connect(sender, signal, manager, [&manager](){ use(manager.res1, ...); });
        // or, directly capture the resources, not the handle
        

        因此,您正在捕捉manager的部分状态。


        在最常见的情况下,当没有context个对象可用时,如果lambda捕获的对象在连接中存活的可能性,则必须通过弱指针捕获它们,并尝试在尝试访问它们之前将这些指针锁定在lambda中。

        在特定的线程/事件循环中运行仿函数

        很快:指定上下文对象时,仿函数将运行到上下文的线程,就像使用接收器对象的普通连接一样。实际上,请注意,带有上下文connect重载也是采用连接类型(而没有上下文的那个也不需要连接 - 连接始终是直接的)。

        同样,这很有用,因为QObject不是可重入的或线程安全的,并且您必须仅在其所在的线程中使用QObject。如果您的仿函数访问另一个线程中的对象,则必须在该线程中执行;将该对象指定为上下文可以解决问题。