What is parent for in Qt?

北战南征 提交于 2019-12-03 10:35:52
Vitor

Besides helping with draw order in GUI objects, it also helps with memory management, so that when you destroy a QObject, all of it's children are destroyed too. See http://doc.qt.io/qt-4.8/objecttrees.html for more details. When something changes in the parent (e.g. when it is resized), it can notify its children to update themselves too.

To answer your question, you're not required to set the parent for everything (that's why it's an optional parameter, after all), but most of the time it's better to set it correctly.

Kuba Ober

Firstly, a QWidget is a QObject, and QObjects are nodes in a QObject tree. The child nodes are memory-managed by the parent, unless you deallocate them before the parent has a chance to do so. Thus, memory management is one reason for widgets, or any other QObjects, to have a parent.

Secondly, visible parentless widgets are always top-level windows. Conversely, it's impossible to have a non-top-level widget that is parentless. When you show a parentless widget, it acquires its own window. The opposite is not necessarily true - it's possible to give a child widget a Qt::Window flag, and it becomes a top-level window as well.

The corollary is that any widget contained in other widgets has a parent - otherwise it'd be a top-level window. This parent might not be set explicitly by you, but it's set nevertheless.

I think that your question can be rephrased as: When do I need to explicitly give widget constructor a parent? The answer is:

  1. Whenever the widget is a top level window that you intend to have a parent. Such windows are not subject to layout management, so there's no mechanism to set that parent for you. Top-level transient dialogs need to have parents so that they are properly positioned in relation to the parent window.

  2. Whenever you have a child widget not subject to layout management.

Widgets subject to layout management are parented upon insertion into a layout:

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();
}

Finally, not all widgets or QObjects need to be explicitly created on the heap. Since all QObject-derived classes in Qt (and many other classes, too!) use the PIMPL idiom, when you allocate them individually on the heap, you're really doing the heap allocation twice. First you allocate the instance of the class - sometimes the instance is as small as a pointer or two - and then the class's constructor allocates its PIMPL. Explicit heap allocation is a case of premature pessimization.

To avoid this pessimization, your Widget should look as follows:

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);
}

If you wished to use the PIMPL idiom, you could do that, too:

// 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!

Of course, you should be using QDialogButtonBox instead of all this :)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!