setupUi()背后的设计原因

时间:2014-02-20 13:49:09

标签: c++ qt raii

我想知道为什么,在您看来, Qt 工程师决定将setupUi()方法放在每个生成的表单中。

(对于那些不知道Qt如何工作的人:setupUi()是一种在动态存储器中分配表格内部的每个按钮,texbox,widget的方法。每个元素的引用都存储在指针成员变量中。甚至没有初始化为nullptr并且它被放置在public:访问修饰符下,因此在您实际调用setupUi()之前看起来也有点危险情况

在我的新意见中,这完全违反了 RAII 原则,因为这意味着任何Ui_Whatever类实际上都不是由构造函数调用构造的:构造函数只是分配类本身,但是在我们调用之前它是不可用的Ui_Whatever::setupUi()

这可能是选择拥有的背后的设计理由:

  • 一个空的编译器生成的default-constructor
  • 进行真实构造的方法

(我问'因为我无法弄清楚自己是否有任何正当理由)

提前致谢!!

3 个答案:

答案 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都可以有所不同。 在打开构造函数的大括号后,对象的动态和静态类型总是相同的。