setupUi()的设计原因

3
我想知道为什么在你的看法中,Qt 工程师决定将setupUi()方法放在每个生成的表单中。
(对于那些不了解 Qt 如何工作的人来说:setupUi()是一个方法,它动态分配了在表单中的每个按钮、文本框和小部件。每个元素的引用被存储在指针成员变量中,这甚至没有初始化为nullptr,并且它被放置在public:访问修饰符下,因此这看起来像是一个有点危险的情况,直到您实际调用setupUi())。
在我这个新手的看法中,这完全违反了RAII原则,因为这意味着任何Ui_Whatever类实际上并没有通过其构造函数调用进行构造:构造函数只是分配了类本身,但在我们调用Ui_Whatever::setupUi()之前它是无法使用的。
可能选择具有以下设计原因之一:
  • 一个空的编译器生成的默认构造函数。
  • 一个执行真正构造的方法。
(我问这个问题是因为我自己无法想出任何有效的理由)
先谢谢你了!

出于传统原因吧。 - Simple
3个回答

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
我会把你的回答理解为“setupUi()双重构造没有有效的理由”。 - nyarlathotep108

1

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

通过将初始化工作委托给setupUi方法,您可以精确控制小部件何时被实例化。如果Ui类的构造函数进行实例化,您可能需要在那个时间之前执行一些设置,但这是不可能的。

Ui类中的指针不是拥有指针。您无需显式删除它们。Ui类中的所有对象指针都指向由其他QObject实例拥有的QObject实例。所有权树的根是您传递给setupUi的小部件。 RAII实际上是委托给拥有的QObject实例;在setupUi中没有理由重复这个过程。

如果您坚持要有一个已初始化的Ui对象,您可以修改uic工具以在构造函数中将指针初始化为nullptr。这是一个5-10行的更改,我留给读者自己决定 :) 就我而言,这是对CPU周期的浪费,但在调试版本中可能会有用。


删除机制可以,但我真的不喜欢两阶段初始化机制:无论在调用setupUi之前需要做什么设置,我也应该能够在调用之后进行...由于没有办法传递其他参数给setupUi,因此它也不应该参与其中... - nyarlathotep108
@nyarlathotep108 Qt 的美妙之处在于您可以更改 uic 以发出恰好想要的代码。 这不是一个很大的变化-您只需将父窗口小部件参数和 setupui 的主体移动到 ui 类的构造函数中即可。 如果您认为值得这样做,那就去做吧 :) 我个人也使用了一个相当严格定制的 Qt 版本,带有几十个补丁程序。 Qt 附带所有工具的源代码-与大多数商业开发框架不同,其中您可能具有库源代码,但没有工具源代码。 - Kuba hasn't forgotten Monica
1
@Kuba_Oder 谢谢,很有趣...你能提供一个关于如何做到这一点的指南链接吗? - nyarlathotep108
@nyarlathotep108 没有指南。1. 为自己编译Qt。2. 在Qt Creator中打开uic项目,找出代码输出发生的位置并进行调整。这不是很难理解的代码。 - Kuba hasn't forgotten Monica

0
UI子对象的一或两阶段初始化背后的问题在于setupUi(this)中的代码需要表单(this参数)的动态类型为表单的真实静态类型(类)。但是,在父类和成员的初始化期间,它是父类的静态类型,而不是表单本身。 因此,在表单对象构建的初始化阶段调用所有虚函数调用和RTTI时可能会有所不同。 在构造函数的打开括号之后,对象的动态和静态类型始终相同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接