【问题标题】:Qt: Connect inside constructor - Will slot be invoked before object is initialized?Qt:在构造函数内连接 - 在初始化对象之前会调用插槽吗?
【发布时间】:2015-06-03 15:54:26
【问题描述】:

我正在学习 Qt 框架 (C++),并且想知道 QT 是否有任何机制来保护插槽在对象完全初始化之前不被调用。

考虑 A 类构造函数:

A::A() {
    mTreeView = new QTreeView();
    ...
    connect(mTreeView, &QTreeView::customContextMenuRequested,
        this, &A::OnContextMenuRequested);
    ...
}

我担心用户可以在 A 的构造函数完成之前右键单击树视图。另一个上下文如下:

A::A() {
    mQObj = new MyQObject();
    connect(mQObj, &MyQObject::SomeEvent, this, &A::OnEvent);
}

A::InitB() { mB = new B(); }

A::OnEvent() { mB.doSomething(); }

这里可以在 InitB() 运行之前调用 doSomething() 方法。

我需要担心这种情况吗?如果是这样,有没有办法避免这些问题,而不必先初始化所有对象,然后再返回并分别连接事件?

【问题讨论】:

  • 如果我的理解是正确的,你必须先调用mTreeView->show(),然后用户才能与之交互。
  • 这是有道理的。因此,我可以通过仅在构造函数末尾显示 UI 元素来降低风险。然而,关于非用户交互事件的问题仍然存在。插槽可以从另一个对象以编程方式发送的信号调用。
  • 这是可能的,你必须设计你的代码来防止这种情况发生。
  • 您可能只需要担心这个问题,如果您有多个线程正在运行并且其中至少一个可以访问您的对象。 This SO post 对此进行了更多解释。

标签: c++ qt event-handling signals-slots


【解决方案1】:

在大多数情况下,您不必担心这种情况,因为事件是在同一个线程中传递的。没有你需要关心的“隐藏的多线程”。如果您没有在 A 的构造函数中显式调用导致事件被处理的函数,那么您是安全的,并且您当前的方法、槽等的执行在处理下一个事件之前完成。

也就是说,处理新事件并因此执行其他代码(事件处理程序、插槽)的情况是:

  1. 当前事件处理程序、槽等的执行完成(您创建A 的代码)并且Qt 返回事件循环以等待下一个事件。在您的情况下,这是在 A 实例完全构造之后。
  2. 你启动一个本地事件循环(创建一个QEventLoop 对象并调用 exec())
  3. 你打电话给QCoreApplication::processEvents()
  4. 您在 QDialog 上调用 exec()

1) 是 Qt 运行的正常模式:您启动 app.exec(),它会启动事件循环。之后的一切都由事件(用户输入、定时器、I/O)直接或间接触发。事件发生并被添加到事件循环的事件队列中。事件循环调用事件的事件处理程序。当事件处理程序完成时,事件循环选择下一个事件并为其调用处理程序。

所以一切都以有序的方式发生,一个接一个的事件,除非其中一个事件处理程序(例如,监听按钮的 clicked() 信号的插槽)执行 2、3 或 4 之一。然后 Qt 处理下一个事件就地,即其中 exec() 或 processEvents() 被调用。相应地执行事件处理程序/插槽。 然后 exec()/processEvents() 返回。现在,不调用 exec()/processEvents() 的所有确定性都消失了,不幸的是:用户可能做了随机的事情,事情可能被任意更改或删除(即使是 this 指针,如果用户关闭窗口, 例如)。因此,尤其是 2) 和 3) 容易出错并且通常会导致严重的问题,因此我会尽可能避免使用它们,或者至少要意识到潜在的问题。

现在有一种情况是您自己使用多线程。由于所有实际的 UI 和相关的用户事件都在单个主线程中处理,因此这里的多线程通常意味着您有线程执行非 UI 工作,并且通过调用 UI 线程中的对象上的函数来与您的 UI 线程交互,修改数据由两个线程共享,或使用跨线程信号/槽连接。

  • 如果您不使用信号/插槽,而是直接从辅助线程调用方法到 UI 线程或修改共享数据,则适用通常的多线程规则:除非您知道自己在做什么和使用什么,否则事情会大错特错相应地同步。 Qt 的 UI 类不是线程安全的。
  • 如果您使用信号/槽,则调用是 /queued/,即如果线程 B 发出信号并且您在主线程的槽中接收它,则槽的调用以与用户相同的方式传递事件:适用与用户事件相同的规则。除非您执行 2、3、4 之一,否则在您的事件处理程序/插槽返回之前不会调用该插槽。所以跨线程信号/槽连接是/消息传递/通过事件循环传递消息的一种方式。

【讨论】:

    猜你喜欢
    • 2011-09-13
    • 1970-01-01
    • 2012-07-23
    • 2021-11-06
    • 1970-01-01
    • 2010-10-01
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多