【问题标题】:Design reason behind setupUi()setupUi() 背后的设计原因
【发布时间】:2014-02-20 13:49:08
【问题描述】:

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

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

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

这可能是选择拥有的设计原因:

  • 一个空的编译器生成的默认构造函数
  • 一种真正构造的方法

(我问是因为我根本想不出任何正当理由)

提前致谢!!

【问题讨论】:

  • 可能是遗留原因。

标签: c++ qt raii


【解决方案1】:

我同意,这完全违反了 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-Element 的值进行初始化(这样我可以在 QtDesigner 中定义初始值,而不是在那里使用假值并在启动时覆盖它),但不能为此使用 RAII,因为 GUI 只会在 MainWindow 的构造函数体内初始化。

因此,我不同意 Kuba Ober 的观点,您无法完全控制您希望 GUI 何时初始化,因为您只能在构造函数主体内执行此操作,而不能在初始化列表中执行(并且在 RAII 之后,初始化列表为可能大部分初始化应该发生的地方)。但幸运的是,C++ 很棒,我们可以为此编写一个解决方法(如上文所述)。我也认为这比改变uic 的方式要好得多,因为一旦其他人尝试编译您的代码,他们就会看到一个很大的惊喜。

【讨论】:

  • 我会将您的回答视为“setupUi() 双重 fase 构造没有正当理由”。
【解决方案2】:

Ui 类并不是一个真正成熟的 C++ 类,它只是一个包装器,应该成为其他东西的一个组成部分。

通过将初始化委托给setupUi 方法,您可以准确控制小部件何时被实例化。您可能需要在此之前执行一些设置,如果 Ui 类的构造函数正在执行实例化,则无法执行此操作。

Ui 类中的指针不是拥有指针。您永远不必明确删除它们。 Ui 类中的所有对象指针都指向由其他QObject 实例拥有的QObject 实例。所有权树的根是您传递给setupUi 的小部件。 RAII 有效地委托给拥有的QObject 实例;没有理由在 setupUi 中复制它。

如果你坚持有一个初始化的 Ui 对象,你可以修改uic 工具在构造函数中对指针进行nullptr-initialize。这是一个 5-10 行的更改,我留给读者看中:) 就我而言,这是对 CPU 周期的浪费,但也许它在调试版本中很有用。

【讨论】:

  • 删除机制没问题,但我真的不喜欢两步初始化机制:在setupUi之前我需要做的任何设置,我应该也可以在调用之后做。 ..由于没有办法将其他参数传递给setupUi,所以无论如何也不应该涉及...
  • @nyarlathotep108 Qt 的美妙之处在于您可以更改uic 以准确地发出您想要的代码。这不是一个大的变化 - 您只需将父小部件参数和 setupui 的主体移动到 ui 类的构造函数。如果它值得为你做,那就去做吧:) 我个人也使用了一个经过相当严格调整的 Qt 版本,有几十个补丁。 Qt 随附所有工具的源代码 - 与大多数商业开发框架不同,您可能有库源代码,但没有工具源代码。
  • @Kuba_Oder 谢谢,这很有趣......你能链接一个关于如何做到这一点的指南吗?
  • @nyarlathotep108 没有指南。 1.自己编译Qt。 2. 在 Qt Creator 中打开 uic 项目,找出代码输出发生的位置,并对其进行调整。这不是很难理解的代码。
【解决方案3】:

UI子对象一两阶段初始化的问题在于setupUi(this)中的代码需要表单的动态类型(this参数)为真表单的静态类型(类)。但是在父类和成员的初始化过程中,它是父类的静态类型,而不是表单本身。 所以在表单对象构造的初始化阶段调用时,所有的虚函数调用和 RTTI 可以不同地工作。 打开构造函数的大括号后,对象的动态和静态类型总是相同的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-01
    • 2018-06-07
    • 1970-01-01
    • 2011-01-31
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多