21

Almost every QtWidgets class can have parent. And usually it's optional to set parent at object initialization. For example,If I create a class that inherits QWidget class, I will do the following on the constructor:

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

I can set or not set parent. I can set cancel_button to be a child of hbox. I can too set cancel_button to be a child of yes_button, but I think it's a bad thing to do.

What's the point of this? And, is it really necessary to set parent for every QWidget based class that I create?

Mas Bagol
  • 3,597
  • 7
  • 34
  • 63

2 Answers2

25

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.

djvg
  • 5,523
  • 3
  • 36
  • 64
Vitor
  • 2,531
  • 21
  • 37
  • Is there any example that I should set a parent `QWidget` based class. A case that the program won't work properly without set the parent? – Mas Bagol May 20 '15 at 15:46
  • 5
    If you don't set the parent correctly, layout managers don't work. Another problem is that in your example, if you destroy your QWidget, you'll have to destroy yes_button manually, or it will leak. If parent was set, Qt would take care of that for you – Vitor May 20 '15 at 15:49
  • 2
    Note that there is a special case with `QWidget`. If it doesn't have a parent it is converted to a top-level window (see [here](http://doc.qt.io/qt-4.8/qobject.html#QObject)). – rbaleksandar Feb 20 '16 at 19:01
10

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 :)

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 88,505
  • 13
  • 129
  • 275
  • Very helpful. Does this mean that the upvoted comment by @vitor above ("If you don't set the parent correctly, layout managers don't work. ...") is incorrect? – djvg Mar 22 '18 at 10:20
  • 1
    The parent needs to be set correctly. It just doesn't mean that you need to write code to do so. Adding a widget to the layout always sets the parent correctly. So vitor's statement isn't necessarily incorrect - it's just unlikely ever to be true. You have to write code to change the parent post-factum for that statement to become true. It won't happen by omission or accident. – Kuba hasn't forgotten Monica Mar 22 '18 at 20:04