Qt:Connect inside constructor - 在初始化对象之前会调用插槽吗?
Qt: Connect inside constructor - Will slot be invoked before object is initialized?
我正在学习 Qt 框架 (C++),想知道 QT 是否有任何机制来保护槽在对象完全初始化之前不被调用。
考虑Class一个构造函数:
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() 方法。
我需要担心这种情况吗?如果是这样,有没有一种方法可以避免这些问题,而不必先初始化所有对象,然后返回并分别连接事件?
在大多数情况下您不必担心这种情况,因为事件是在同一个线程中传递的。您不必关心任何 "hidden multithreading" 的事情。如果您没有在 A 的构造函数中显式调用导致事件被处理的函数,那么您是安全的,并且您当前的方法、槽等的执行在下一个事件被处理之前完成。
也就是说,处理新事件并因此执行其他代码(事件处理程序、槽)的情况是:
- 当前事件处理程序、插槽等的执行完成(您创建的代码
A
)并且Qt returns进入事件循环等待下一个事件。在您的情况下,那是在完全构建 A 实例之后。
- 你启动一个本地事件循环(创建一个 QEventLoop 对象并调用 exec())
- 你打电话给QCoreApplication::processEvents()
- 您在 QDialog
上调用 exec()
1) 是 Qt 运行的正常模式:您启动 app.exec(),这会启动事件循环。之后的一切都由事件(用户输入、计时器、I/O)直接或间接触发。事件发生并被添加到事件循环的事件队列中。事件循环调用事件的事件处理程序。当事件处理程序完成时,事件循环选择下一个事件并为其调用处理程序。
所以一切都以有序的方式发生,一个事件接一个事件发生,除非事件处理程序之一(例如,监听按钮的 clicked() 信号的插槽)执行 2、3 或 4 中的一个。然后 Qt 处理下一个事件就地,即调用 exec() 或 processEvents() 的位置。相应地执行事件 handlers/slots。 然后 exec()/processEvents() returns。现在,如果不调用 exec()/processEvents(),所有确定性都消失了,不幸的是:用户可能做了随机的事情,事情可能被任意更改或删除(甚至 this
指针,如果用户关闭window,例如)。因此,尤其是 2) 和 3) 容易出错,通常会导致严重的头痛,所以我会尽可能避免使用它们,或者至少要意识到潜在的问题。
现在出现了您自己使用多线程的情况。由于所有实际的 UI 和相关的用户事件都在单个主线程中处理,这里的多线程通常意味着您有线程执行非 UI 工作,并且与您的 UI 线程交互在 UI 线程中的对象上调用函数,修改两个线程共享的数据,或使用跨线程 signal/slot 连接。
- 如果您不使用 signal/slots,而是直接从辅助线程调用方法到 UI 线程或修改共享数据,通常的多线程规则适用:除非您知道自己在做什么并相应地使用同步。 Qt 的 UI 类 不是线程安全的。
- 如果你使用 signal/slots,调用是 /queued/,即如果线程 B 发出一个信号并且你在主线程的一个槽中接收到它,槽的调用在与用户事件相同的方式:适用与用户事件相同的规则。除非您执行 2、3、4 之一,否则不会在您的事件 handler/slot returns 之前调用插槽。所以跨线程 signal/slots 连接是一种/消息传递/消息通过事件循环传递的方式。
我正在学习 Qt 框架 (C++),想知道 QT 是否有任何机制来保护槽在对象完全初始化之前不被调用。
考虑Class一个构造函数:
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() 方法。
我需要担心这种情况吗?如果是这样,有没有一种方法可以避免这些问题,而不必先初始化所有对象,然后返回并分别连接事件?
在大多数情况下您不必担心这种情况,因为事件是在同一个线程中传递的。您不必关心任何 "hidden multithreading" 的事情。如果您没有在 A 的构造函数中显式调用导致事件被处理的函数,那么您是安全的,并且您当前的方法、槽等的执行在下一个事件被处理之前完成。
也就是说,处理新事件并因此执行其他代码(事件处理程序、槽)的情况是:
- 当前事件处理程序、插槽等的执行完成(您创建的代码
A
)并且Qt returns进入事件循环等待下一个事件。在您的情况下,那是在完全构建 A 实例之后。 - 你启动一个本地事件循环(创建一个 QEventLoop 对象并调用 exec())
- 你打电话给QCoreApplication::processEvents()
- 您在 QDialog 上调用 exec()
1) 是 Qt 运行的正常模式:您启动 app.exec(),这会启动事件循环。之后的一切都由事件(用户输入、计时器、I/O)直接或间接触发。事件发生并被添加到事件循环的事件队列中。事件循环调用事件的事件处理程序。当事件处理程序完成时,事件循环选择下一个事件并为其调用处理程序。
所以一切都以有序的方式发生,一个事件接一个事件发生,除非事件处理程序之一(例如,监听按钮的 clicked() 信号的插槽)执行 2、3 或 4 中的一个。然后 Qt 处理下一个事件就地,即调用 exec() 或 processEvents() 的位置。相应地执行事件 handlers/slots。 然后 exec()/processEvents() returns。现在,如果不调用 exec()/processEvents(),所有确定性都消失了,不幸的是:用户可能做了随机的事情,事情可能被任意更改或删除(甚至 this
指针,如果用户关闭window,例如)。因此,尤其是 2) 和 3) 容易出错,通常会导致严重的头痛,所以我会尽可能避免使用它们,或者至少要意识到潜在的问题。
现在出现了您自己使用多线程的情况。由于所有实际的 UI 和相关的用户事件都在单个主线程中处理,这里的多线程通常意味着您有线程执行非 UI 工作,并且与您的 UI 线程交互在 UI 线程中的对象上调用函数,修改两个线程共享的数据,或使用跨线程 signal/slot 连接。
- 如果您不使用 signal/slots,而是直接从辅助线程调用方法到 UI 线程或修改共享数据,通常的多线程规则适用:除非您知道自己在做什么并相应地使用同步。 Qt 的 UI 类 不是线程安全的。
- 如果你使用 signal/slots,调用是 /queued/,即如果线程 B 发出一个信号并且你在主线程的一个槽中接收到它,槽的调用在与用户事件相同的方式:适用与用户事件相同的规则。除非您执行 2、3、4 之一,否则不会在您的事件 handler/slot returns 之前调用插槽。所以跨线程 signal/slots 连接是一种/消息传递/消息通过事件循环传递的方式。