1

This code which does not dynamically allocate memory, does not show any label on the window.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QLabel l;
    l.setText ("cdsadsaf");
    l.setParent (this);
}

After dynamically allocating memory, the label shows up.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QLabel *label = new QLabel(this);
    label->setText("first line\nsecond line");
}

Why is dynamic memory allocation necessary for QLabel to work?

Aquarius_Girl
  • 21,790
  • 65
  • 230
  • 411

3 Answers3

5

It's not required. You have typical scope problem here.

The first case creates the QLabel on the stack and it "dies" when you exit your constructor.

In the second it keeps on living 1)because it's dynamically allocated and 2)you actually assign a parent to it - that is your main window. If you don't do 2), the effect will be the same as with the first case but worse - you will create a memory leak:

Memory leak

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QLabel *label = new QLabel(); # no parent
    label->setText("first line\nsecond line");
}

No memory leak due to a parent being assigned to the label

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QLabel *label = new QLabel(this); # main window is the parent and will take care of destroying the label when its own destructor is called
    label->setText("first line\nsecond line");
}

You can avoid allocating the QLabel on the heap and yet still be able to use it by just moving it to a broader scope. Since your label is to be displayed in a main window, you can create a class member label. No dynamic allocation required because it will keep living as long as your window instance is alive.

class MainWindow : public QMainWindow
{
...
private:
  QLabel l;
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    this->l.setText ("cdsadsaf");
}

As mentioned in the comment (thanks again!) setParent(...) is not required here unless you want a parent-child relation for something else than memory management. See comments below.

rbaleksandar
  • 8,713
  • 7
  • 76
  • 161
  • If you call `setParent` on widget that is class member, won't it be deleted twice? Once by assigned parent and once as a means of ordinary object destruction? I think that's worth clarifying. – el.pescado - нет войне May 11 '17 at 05:36
  • You are right. When I copy-pasted the code I forgot to remove that line. It will actually break all hell because `delete` is called and doing that on something that is allocated on the stack is a bad move. Thanks for pointing this out! – rbaleksandar May 11 '17 at 05:53
  • 1
    Actually, it's fine. Child objects disassociate themselves from parent objects when deleted. When MainWindow is deleted, destructors of member variables are called first, so `l` unlinks itself from `MainWindow` prior to being destructed, so when `MainWindow` destructor is called, `l` is no longer its child. But still, it's worth pointing that out, expecially since there are some nitpicks. [Qt docs: object trees](http://doc.qt.io/qt-5/objecttrees.html) – el.pescado - нет войне May 11 '17 at 06:07
  • BTW. Instead of calling `setParent` you may just use initialisation list: `MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), l(this) { ... }` – el.pescado - нет войне May 11 '17 at 06:13
  • You have to show the label if you don't set a parent to it. Otherwise it will remain invisible. Also [without a parent it will be a separate window](http://doc.qt.io/qt-5/qwidget.html#details), it won't be inside the `MainWindow`. And as el.pescado stated, [it won't be deleted twice if you set a parent to it](http://doc.qt.io/qt-5/objecttrees.html). – thuga May 11 '17 at 07:44
  • Showing the label is not part of the question. You are assuming that the OP wants to do that from inside the constructor but it is possible that this is done somewhere else. This is something that the OP should clarify. I worked only with the code that was provided. As for a parentless widget shown as a top-level one - you are right, but I found the issue with the memory management more pressing so didn't include this info. – rbaleksandar May 11 '17 at 07:57
  • Btw thanks for the clarification. I actually tested it right after I posted the answer and indeed it didn't cause a problem. I removed this part from my answer. – rbaleksandar May 11 '17 at 08:00
  • Note that the concept of "stack" is not a part of the C++ standard and is **unnecessary** to explain what's going on. The object lifetime and scoping has nothing to do with stacks. A stack is not necessary for a C++ implementation. – Kuba hasn't forgotten Monica May 11 '17 at 12:35
  • **It's not true that setting a parent always results in double deletion**. If something destroys a `QObject` before its parent is destroyed, then the parent will not destroy it because the parent now doesn't have that object as a child. I don't get why this leads to any sort of confusion. `~QObject()` will only destroy children **that it has at that moment**. If a child is gone before then, it won't be on its `children()` list and won't get destroyed. You might the order of things wrong by misunderstanding C++ semantics, but that's just that: a fundamental misunderstanding of how C++ works. – Kuba hasn't forgotten Monica May 11 '17 at 12:39
2

This code which does not dynamically allocate memory, does not show any label on the window.

That's because the label goes out of scope as soon as you return from the constructor. The label's lifetime is annotated below. The label is a QLabel itself.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QLabel label;                // label is born
    label.setText ("cdsadsaf");  // label is alive
    label.setParent (this);      // label is alive
}                                // label dies

After dynamically allocating memory, the label shows up.

That's because the label doesn't go out of scope. The pointer to it does, but that doesn't matter. Note that label is merely a pointer, and the QLabel object exists independently of the pointer to it.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QLabel *label = new QLabel(this);          // label is born, QLabel is born
    label->setText("first line\nsecond line"); // label is alive, QLabel is alive
}                                              // label dies, QLabel is alive

Why is dynamic memory allocation necessary for QLabel to work?

It's not. You happened to give the QLabel a chance to stay alive as a consequence of using dynamic allocation, but that's just a coincidence.

You can make the label a part of the parent object itself - it won't necessitate a separate allocation then. The compiler will manage the memory for you.

#include <QtWidgets>

class MainWindow : public QMainWindow {
  QWidget m_central;
  QGridLayout m_layout{&m_central};
  QLabel m_label{"Hello, World"};
public:
  MainWindow(QWidget * parent = {}) : QMainWindow{parent} {
    m_layout.addWidget(&m_label, 0, 0);
    setCentralWidget(&m_central);
  }
};

int main(int argc, char ** argv) {
  QApplication app{argc, argv};
  MainWindow w;
  w.show();
  return app.exec();
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
1

When you create a QLabel on the stack, it gets deleted when the function returns. By the time the parent widget updates its display the QLabel is not around any longer.

Creating it on the heap allows it to live beyond the call to the function.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    Because the word "stack" is nowhere in the C++ standard, and is not necessary to describe what happens. The concept that everyone seems to forget about is that of **scope**. The object goes out of scope and ceases to exist. It **doesn't matter** how the C++ compiler implements the going out of scope. The compiler could be compliant and generate code that puts all automatic variables on the heap. The stack is not necessary to explain what's going on - it's an implementation detail. – Kuba hasn't forgotten Monica May 11 '17 at 12:25
  • @KubaOber, I appreciate your point of view. Thanks for adding the clarification. FWIW, the C++11 standard mentions stack in quite a few place. – R Sahu May 11 '17 at 15:06
  • 1
    The word "stack" in the C++ standard is not **the** stack. As used in the C++ standard other than for ``, it refers *only* to stack unwinding and is an abstract term. It implies nothing about the presence of any sort of stack primitive in the platform itself. It is also not used to describe object lifetime other than when referring to stack unwinding - a process that's related to exception handling and has nothing to do with object lifetime as it relates to scoping. The standardese is precise: "stack unwinding" is always used as a phrase. The term "stack" doesn't appear alone. – Kuba hasn't forgotten Monica May 11 '17 at 16:02