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