当我们使用 Qt 时,我们总是从 Qt 类派生一个新类,并为我们的新类创建一个实例。例如,为了使用 QMainWindow 类,我们从 QMainWindow 派生一个新类,例如称为 MW,并从 MW 创建一个实例。
我的问题是为什么我们不在这种情况下从 QMainWindow 从基类创建一个实例?
当我们使用 Qt 时,我们总是从 Qt 类派生一个新类,并为我们的新类创建一个实例。例如,为了使用 QMainWindow 类,我们从 QMainWindow 派生一个新类,例如称为 MW,并从 MW 创建一个实例。
我的问题是为什么我们不在这种情况下从 QMainWindow 从基类创建一个实例?
嗯,我已经使用 Qt 多年了,从来没有想过这个问题——好问题。
我会说如果您可以使用 QMainWindow 而不对其进行子类化 - 继续。但我认为基础 QMainWindow 类本身的用处是相当有限的。
我通常在子类中有成百上千行代码,比如创建菜单、连接信号和插槽等。如果不在子类中,我不确定这些代码在逻辑上会存在于何处。
QMainWindow 也有可能具有纯虚拟,因此您必须覆盖它。不过不知道,没看过。
希望这能回答我理解的问题。
您不需要子类化 QWidget / QMainWindow,但是用于创建新项目和新 UI 文件的 QtCreator 助手会这样做。
原因很简单:
它强制执行更简洁的代码。您将此小部件/窗口的行为放在一个类中,而不是在类之外。因此,您的窗口的每个实例都具有相同的行为。如果您只是简单地实例化一个 QWidget / QMainWindow 并从外部添加行为(通过连接到您需要从外部创建的小部件的信号),那么这些实例可能具有不同的行为,这显然不是我们所理解的面向对象设计。
可以将子小部件添加为成员。如您所知,只有在子类化时才能将成员添加到类中。不继承它意味着您需要使用小部件/主窗口将这些指针存储在代码中,从而导致代码错误。请注意,您不必存储指向子小部件的指针,因为它们被放入 QObject 树并由 Qt 内部管理。但是连接到信号、从 QLineEdit 读取文本等值等等,只有在您拥有指向这些小部件的指针时才会发生。
处理事件需要子类化[编辑:也可以使用事件过滤器来实现,请参阅评论],因为它们是作为虚拟方法而不是作为信号实现的。这还包括自定义绘画,这是通过重新实现 virtual 来完成的paintEvent
。处理事件还包括对鼠标点击做出反应(除了点击子小部件,它们自己处理)、鼠标滚轮旋转、按键、最小化和最大化等等。如果您启用它,您可以使用信号槽连接处理的窗口上唯一的“操作”是关闭它并请求上下文菜单(右键单击)。所以这两个不一定需要子类化。
在良好的面向对象代码的上下文中更一般地说,创建子类的规则显然如下:
如果你有相同事物的实例,在相同的输入/属性上表现相同,它应该是相同的类型。
但是一旦你有实例,你就想表现得不同,不仅取决于不同的输入/属性,它应该是另一种类型(子类)。
只要您需要更改类的某些(已定义的)行为(“覆盖它”)或添加一些成员,它就必须是另一种类型(子类)。
说一个应该“总是”子类听起来很危险。我宁愿遵循“尽可能复合,必要时继承”。
除了在少数情况下它可以充当容器之外,QWidget 本身是无用且空的,并且它意味着专门用于实际做某事。
通过从外部添加中央小部件、菜单栏等,QMainWindow 可以完美地使用而无需子类化。由于来自工具栏按钮、菜单条目等的 QAction 需要连接到某个地方,因此主窗口通常包含作用于这些操作的插槽,这表明子类化。如果没有失控的话,有一个连接不同部分的中心位置,调出对话等是有点自然和好的 IMO。即,如果主窗口包含比转发动作触发器和简单地弹出对话框更多的逻辑,则应考虑将该逻辑移出主窗口。
尽管还有许多其他小部件不是为子类化而设计的,并且默认情况下对它们进行子类化毫无意义,没有充分的理由。子类化 QLineEdit、QPushButton(如果您需要特殊按钮,则子类 QAbstractButton)、QCheckBox 等几乎没有意义,因为它们不提供可以重新实现以更改行为的特定于小部件的虚拟方法。除非您重新实现虚拟方法,否则组合几乎总是实现目标的更好方法。是的,在某些情况下,重新实现 fooEvent() 可能比注册事件过滤器更直接。在这些情况下,这样做,但每当你需要 QLineEdit 时创建一个“MyLinedit”对我来说是荒谬和不好的做法。