21

几乎每个 QtWidgets 类都可以有父类。通常在对象初始化时设置父级是可选的。例如,如果我创建一个继承类的QWidget类,我将在构造函数上执行以下操作:

Widget::Widget(QWidget* parent): QWidget(parent) {
    hbox = new QHBoxLayout(this);
    yes_button = new QPushButton("&Yes");
    no_button = new QPushButton("&No", this);
    cancel_button = new QPushButton("&Cancel", hbox);
}

我可以设置或不设置父级。我可以设置cancel_buttonhbox. 我也可以cancel_button成为 的孩子yes_button,但我认为这是一件坏事。

这有什么意义?而且,真的有必要为QWidget我创建的每个基础类设置父类吗?

4

2 回答 2

28

除了帮助 GUI 对象中的绘制顺序外,它还有助于内存管理,因此当您销毁 QObject 时,它的所有子对象也会被销毁。有关详细信息,请参阅http://doc.qt.io/qt-4.8/objecttrees.html 。当父级中的某些内容发生变化时(例如调整大小时),它可以通知其子级也进行更新。

要回答您的问题,您不需要为所有内容设置父级(这就是为什么它是一个可选参数,毕竟),但大多数情况下最好正确设置它。

于 2015-05-20T15:37:10.840 回答
11

首先, aQWidget是 a QObjectQObjects 是QObject树中的节点。子节点由父节点管理内存,除非您在父节点有机会这样做之前释放它们。因此,内存管理是小部件或任何其他QObjects 拥有父级的原因之一。

其次,可见的无父小部件始终是顶级窗口。相反,不可能有一个没有父级的非顶级小部件。当您显示无父窗口小部件时,它会获取自己的窗口。相反的不一定是正确的——可以给一个子部件一个Qt::Window标志,它也成为一个顶级窗口。

推论是其他小部件中包含的任何小部件都有一个父窗口 - 否则它将是一个顶级窗口。您可能没有明确设置此父级,但仍然设置了它。

我认为你的问题可以改写为:我什么时候需要明确地给小部件构造函数一个父级?答案是:

  1. 每当小部件是您打算拥有父级的顶级窗口时。此类窗口不受布局管理的约束,因此没有为您设置该父级的机制。顶级瞬态对话框需要有父级,以便它们相对于父窗口正确定位。

  2. 每当您有一个不受布局管理约束的子小部件时。

受布局管理的小部件在插入布局时成为父级:

int main(int argc, char ** argv) {
  QApplication app(argc, argv);
  QWidget window;
  QVBoxLayout layout(&window);
  QLabel label("Hello");
  QPushButton button("Goodbye");
  layout.addWidget(&label);
  layout.addWidget(&button);
  QObject::connect(&button, &QPushButton::clicked, [&app]{ app.quit(); });
  window.show();
  return app.exec();
}

最后,并非所有小部件或QObjects 都需要在堆上显式创建。由于 Qt 中的所有QObject派生类(以及许多其他类!)都使用 PIMPL 习惯用法,因此当您在堆上单独分配它们时,实际上是在进行两次堆分配。首先分配类的实例——有时实例小到一两个指针——然后类的构造函数分配它的 PIMPL。显式堆分配是过早悲观化的一种情况。

为避免这种悲观情绪,您Widget应该如下所示:

class Widget : public QWidget {
  Q_OBJECT
  QHBoxLayout m_layout;
  QPushButton m_yesButton, m_noButton, m_cancelButton;
public:
  Widget(QWidget * parent = 0);
};

Widget::Widget(QWidget * parent) : 
  QWidget(parent),
  m_layout(this),
  m_yesButton("&Yes"),
  m_noButton("&No"),
  m_cancelButton("&Cancel")
{
  m_layout.addWidget(&m_yesButton);
  m_layout.addWidget(&m_noButton);
  m_layout.addWidget(&m_cancelButton);
}

如果你想使用PIMPL idiom,你也可以这样做:

// Widget.h - Interface
class WidgetPrivate;
class Widget : public QWidget {
{
  Q_OBJECT
  Q_DECLARE_PRIVATE(Widget)
  QScopedPointer<WidgetPrivate> const d_ptr;
public:
  Widget(QWidget * parent = 0);
  ~Widget();
};

// Widget.cpp - Implementation 
class WidgetPrivate {
  Q_DISABLE_COPY(WidgetPrivate)
  Q_DECLARE_PUBLIC(Widget)
  Widget * const q_ptr;
  QHBoxLayout layout;
  QPushButton yesButton, noButton, cancelButton;
public:
  WidgetPrivate(Widget * q);
};

WidgetPrivate::WidgetPrivate(Widget * q) {
  q_ptr(q),
  layout(q),
  yesButton("&Yes"),
  noButton("&No"),
  cancelButton("&Cancel")
{
  layout.addWidget(&yesButton);
  layout.addWidget(&noButton);
  layout.addWidget(&cancelButton);
}

Widget::Widget(QWidget * parent) :
  QWidget(parent),
  d_ptr(new WidgetPrivate(this))
{}

Widget::~Widget() {}
// necessary, since WidgetPrivate is unknown to the interface!

当然,您应该使用QDialogButtonBox而不是所有这些:)

于 2015-05-20T18:05:02.340 回答