我想知道为什么,在您看来, Qt 工程师决定将setupUi()
方法放在每个生成的表单中。
(对于那些不知道Qt如何工作的人:setupUi()
是一种在动态存储器中分配表格内部的每个按钮,texbox,widget的方法。每个元素的引用都存储在指针成员变量中。甚至没有初始化为nullptr
并且它被放置在public:
访问修饰符下,因此在您实际调用setupUi()
之前看起来也有点危险情况
在我的新意见中,这完全违反了 RAII 原则,因为这意味着任何Ui_Whatever类实际上都不是由构造函数调用构造的:构造函数只是分配类本身,但是在我们调用之前它是不可用的Ui_Whatever::setupUi()
。
这可能是选择拥有的背后的设计理由:
(我问'因为我无法弄清楚自己是否有任何正当理由)
提前致谢!!
答案 0 :(得分:4)
我同意,这完全违反了RAII原则。但我找到了一个相当简单的解决方法:
template<typename UiClass> class QtRaiiWrapper : public UiClass
{
public:
QtRaiiWrapper(QMainWindow *MainWindow)
{
setupUi(MainWindow);
}
};
...
#include "ui_Main.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
...
private:
// Ui_MainWindow m_Ui; // no RAII
QtRaiiWrapper<Ui_MainWindow> m_Ui; // RAII
};
(可能需要一些改进,但这是基本的想法,对我很有用。)
关于该主题的更多讨论:
Qt选择通过setupUi实现初始化不仅缺乏该组件的RAII,它还可能使RAII无法用于程序的其他部分,这是双坏的。在我的情况下,另一个对象使用GUI元素的值进行初始化(这样我可以在QtDesigner中定义初始值,而不是在那里使用伪值并在启动时覆盖它),但是不能使用RAII,因为GUI只能在我的MainWindow的构造函数体内初始化。
因此我不同意Kuba Ober,你还没有完全控制你何时想要初始化GUI,因为你只能在构造函数体内而不是初始化列表中进行(并且在RAII之后,初始化列表是也许大多数初始化应该发生的地方)。但幸运的是,C ++非常棒,我们可以为此编写一个解决方法(如上所述)。我也认为这比改变uic
的方式好得多,因为一旦其他人尝试编译你的代码,他们就会看到一个很大的惊喜。
答案 1 :(得分:0)
Ui类实际上并不是一个完整的C ++类,它只是一个包装器,应该成为其他东西的组成部分。
通过将初始化降级为setupUi
方法,您可以准确控制小部件何时被实例化。您可能需要在此之前执行某些设置,如果Ui类的构造函数正在进行实例化,则无法执行此操作。
Ui类中的指针不具有指针。您永远不必明确删除它们。 Ui类中的所有对象指针都指向其他QObject
实例拥有的QObject
个实例。所有权树的根是您传递给setupUi
的小部件。 RAII被有效地委托给拥有QObject
个实例;我没有理由在setupUi
中复制它。
如果您坚持使用初始化的Ui对象,则可以修改uic
工具以对构造函数中的指针进行nullptr初始化。这是一个5-10行的变化,我留给读者看中:)这是浪费CPU周期,就我而言,但也许它可能在调试版本中有用。
答案 2 :(得分:0)
UI子对象的一阶段或两阶段初始化背后的问题是 setupUi(this)中的代码需要表单(this
参数)的动态类型表单的真实静态类型(类)。但在父类和成员的初始化期间,它是父类的静态类型,而不是表单本身。
因此,在表单对象构造的初始化阶段调用时,所有虚函数调用和RTTI都可以有所不同。
在打开构造函数的大括号后,对象的动态和静态类型总是相同的。