1

I am new with Qt and i am very confused about how widgets are deleted. I was reading a video and i wanted to show up a QProgressbar while the video frames are being read and then remove this QProgressbar when the video is loaded.

I have done it with 2 different ways:

  1. Using Pointers

    QWidget* wd = new QWidget();
    QProgressBar* pB = new QProgressBar(wd);
    QLabel* label = new QLabel(wd);
    //setting geometry and updating the label and progressbar
    wd->deleteLater();
    wd->hide();
    

    this code is written inside a class and i was assuming when the destructor of this class is called, the widget will be deleted with all of it's children but that didn't happen and everytime i run this function again a new widget is created without hiding or deleting the previous one (NOTE: i have tried to delete the label and progressbar from the widget assuming that they will disappear from inside the widget but this didn't happen "delete(pB);")

  2. Using Objects

        QWidget wd;
        QProgressBar pB(&wd);
        QLabel label(wd);
        //setting geometry and updating the label and progressbar
        wd.deleteLater();
        wd.hide();
    

When i have run the same code but using objects instead of pointers , it has run exactly as i have wanted and everytime i run the function, the old widget is destroyed and a new one is created.

NOTE: -Also when i close the main window, in case of pointers, the widget wd still exists and the program doesn't terminate until i close them manually - In case of Objects, when i close the main window everything is closed and the program is terminated correctly.

I need someone to explain me why is this happening and how if i am having a vector of pointers to widgets to delete all pointers inside that vector without any memory leakage

Tarek
  • 31
  • 1
  • 4
  • 1
    I recommend reading [a book](http://stackoverflow.com/a/388282/3484570) about how memory management works in C++. Then read about [how Qt does it differently](http://doc.qt.io/qt-5/objecttrees.html). – nwp Jul 30 '16 at 11:52
  • Instead of deleting the `QProgressBar` you may simply hide it. You could need it anyway for the next stream. – maxik Jul 30 '16 at 12:59

2 Answers2

0

In typical C++ the rule would be "write one delete for every new". An even more advanced rule would be "probably don't write new or delete and bury that in the RIAA pattern instead". Qt changes the rule in this regard because it introduces its own memory management paradigm. It's based on parent/child relationships. QWidgets that are newed can be given a parentWidget(). When the parentWidget() is destroyed, all of its children will be destroyed. Hence, in Qt it is common practice to allocate objects on the stack with new, give them a parent, and never delete the memory yourself. The rules get more complicated with QLayout and such becomes sometimes Qt objects take ownership of widgets and sometimes they don't.

In your case, you probably don't need the deleteLater call. That posts a message to Qt's internal event loop. The message says, "Delete me when you get a chance!" If you want the class to manage wd just give it a parent of this. Then the whole parent/child tree will get deleted when your class is deleted.

  • In my case, {this} pointer is pointing to a normal c++ class , not inheriting from Qobject. and when the destructor of this class is called , it still does't delete wd or even hide it (in the case of initializing it as a pointer). As i have understood from you that Qt doesn't give much care when i say {delete} to a widget pointer. So how can i delete such a widget from memory. I need to dynamically allocate labels in a QScrollbar and whenever i press a button this QScrollbar is refreshed and new labels are allocated in the QScrollbar after deletion of the old labels – Tarek Jul 31 '16 at 11:16
  • This answer is obsolete, it was obsolete even with C++98. The typical C++ rule is: delegate managing your resources to resource-managing classes, such as smart pointers. You shouldn't be writing any `delete`s by hand - C++ is not C where such was necessary. – Kuba hasn't forgotten Monica Aug 03 '16 at 14:34
  • Fantastic reading comprehension. Did you blank out by sentence two where I talked about RIAA? –  Aug 04 '16 at 21:55
0

It's all really simple. QObject-derived classes are just like any other C++ class, with one exception: if a QObject has children, it will delete the children in its destructor. Keep in mind that QWidget is-a QObject. If you have an instance allocated usingnew`, you must delete it, or ensure that something (a smart pointer!) does.

Of course, attempting to delete something you didn't dynamically allocate is an error, thus:

  1. If you don't dynamically allocate a QObject, don't deleteLater or delete it.

  2. If you don't dynamically allocate a QObject's children, make sure they are gone before the object gets destructed.

Also, don't hide widgets you're about to destruct. It's pointless.

To manage widget lifetime yourself, you should use smart pointers:

class MyClass {
  QScopedPointer<QWidget> m_widget;
public:
  MyClass() :
    widget{new QWidget};
  {
    auto wd = m_widget->data();
    auto pb = new QProgressBar{wd};
    auto label = new QLabel{wd};
  }
};

When you destroy MyClass, the scoped pointer's destructor will delete the widget instance, and its QObject::~QObject destructor will delete its children.

Of course, none of this is necessary: you should simply create the objects as direct members of the class:

class MyClass {
  // The order of declaration has meaning! Parents must precede children.
  QWidget m_widget;
  QProgressBar m_bar{&m_widget};
  QLabel m_label{&m_widget};
public:
  MyClass() {}
};

Normally you'd be using a layout for the child widgets:

class MyClass {
  QWidget m_widget;
  QVBoxLayout m_layout{&m_widget};
  QProgressBar m_bar;
  QLabel m_label;
public:
  MyClass() {
    m_layout.addWidget(&m_bar);
    m_layout.addWidget(&m_label);
  }
};

When you add widgets to the layout, it reparents them to the widget the layout has been set on.

The compiler-generated destructor looks as below. You can't write such code, since the compiler-generated code will double-destroy the already destroyed objects, but let's pretend you could.

MyClass::~MyClass() {
  m_label.~QLabel();
  m_bar.~QProgressBar();
  m_layout.~QVBoxLayout();
  // At this point m_widget has no children and its `~QObject()` destructor
  // won't perform any child deletions.
  m_widget.~QWidget();
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Your answer is very informative and taught me a lot but i still don't understand why delete(wd) wasn't working .. as you say when i use delete(parent widget) it should be destroyed and also it's children but this didn't happen to me at all. This even looked more complicated when i used a vector like {std::vector labelPointers} , it's like impossible to free the memory of this labelPointers vector, can you explain more please? – Tarek Aug 03 '16 at 08:39
  • @Tarek You don't show complete code, so we can't help you here. What you show in the question is wholly insufficient. In other words, all of it works fine for me. Don't use words. Use code. Your examples should be short. Think of 50-75 lines at most, all in one file. Remove everything unnecessary. Look at e.g. [the github repository of my answers](https://github.com/KubaO/stackoverflown) for inspiration. Most of them are very short, and they all should compile. – Kuba hasn't forgotten Monica Aug 03 '16 at 14:33