The ubiquity of memory leaks in GUI applications.

When I was studying C++ as an Independent Student, we were taught that, as a good programming practice, we should always delete any objects which we had created with the ‘new’ allocator, including, to do so in the destructor of any objects, which created those members in their constructor. Just to define a little bit, what this means, I need to state some facts:

  1. In C++, we can declare objects to exist as such, without using ‘new’, and without initializing object pointers to point to them. When we do so, they are allocated on the stack, their constructors are called in turn, and, when our function call returns, the compiler makes sure that the stack is cleared, at which point the compiler also makes sure that the destructor of such objects is called automatically. In much programming, including GUIs that we build with Qt5, this is considered to be the less-favoured way to do it by default…
  2. As an alternative, we can declare a pointer to a class object to exist. This pointer could reside on the stack, or, it could be a member of the class, and thus, be a property of each object. Regardless of where this pointer has been put, we call ‘new’ to create a new object, and the address returned by ‘new’ is stored in the pointer, by our own explicit code, either in the constructor if the pointer is a class member, or elsewhere if the pointer has been created in the middle of a function-call.
  3. The pointer, a member of the class, might just receive an address from someplace else, thus pointing to an object that was allocated by a different object, in which case it’s the responsibility of the object that originated the pointer’s address, also to destruct the object it points to… This can lead to issues because, if the object that created the object being deleted does so, the other object will still hold an address, which does not get updated (at least, using plain-old C++ pointers, not, smart-pointers). And then, such a copied address might erroneously be used again, creating nasty error messages and crashes.

The problem with scenario (2) above is, that even though the pointer will get removed eventually, during program execution, the object that it points to, will remain by default, unless we call ‘delete’ on the pointer, in which case the destructor will be called on the object pointed to, and then, the space the object occupied is deallocated. There are finer points to how all this should be done, which are taught in C++ programming, and which I will not get into here. But a point which I want to make in this posting is that, when using certain GUI libraries, including Qt5, the programmer is creating many C++ objects, including widgets, layout managers, labels, QString objects, and eventually also, brief message-boxes…

The programming style encourages sloppy programming, in which not all objects that have been created, are also destructed and deallocated eventually. Admittedly, I have also fallen victim to this, in recent exercises I assigned myself, outside any formal learning environment. And of course, always conscious of the possibility that I could be making mistakes, I have found several.

The lessons I was given formally taught us, to be so thorough in deallocating, that we should even do so when our program quits. This is good form. But, when creating my Qt5 projects, I have not been doing this. Instead, what I’ve been doing, is to distinguish between two types of memory leaks:

  1. Objects that are not deleted when the program quits, meaning that my main application window possesses a destructor which does nothing,
  2. Objects that will need to be deleted, because they were members (properties) of other objects, which might be specialized windows of my application, or which might otherwise be created and deleted many times, during one execution of the application.

 

(Updated 9/03/2020, 14h20… )

(As of 9/03/2020, 7h35: )

What can happen with GUI applications is, that message boxes, secondary windows, etc., might get created many times during one application run, and contain pointers to objects which they allocated in their own constructors, and which they should delete in their destructors. If they fail to do so, the program will continue to work, but, as the user makes extensive use of the application, it will consume an ever-increasing amount of memory on its heap, and, depending on how far that goes, the user may need to quit the application and start a new session with it, just so that the new instance will consume a reasonable amount of memory again.

If it should happen that the application is merely failing to deallocate something that it allocated using ‘new’, once when it quits, there is no real danger resulting, because the memory was allocated on the heap, and therefore, in the data-segment of the main process. When that process has quit, the O/S will also deallocate its data segment, so that there will be no effect on the O/S afterwards from this.

In certain cases, objects might be created every time a widget is clicked on, and the widget might be of a variety that is meant to be clicked on rapidly and often. Of course in such a case, it actually becomes critical that the objects which were allocated are also destructed, and that, in their destructors, they run ‘delete’ on their member pointers. In fact, in theory, code is possible that creates objects thousands of times within a loop, and deletes them again, in which case those objects must certainly have working destructors…


 

I can show the reader an example, where I succumbed to the temptations of GUI programming, for the programmer to create memory leaks:

 


void MainWindow::showAbout() {
//    QMessageBox *box = new QMessageBox(this);
//    box->setTextFormat(Qt::RichText);
//    box->setText(*message);
//    box->show();
    QMessageBox::information(this, QString("Info"), *message);
}

 

This example shows ‘a wrong way’ to do something, which is commented out, as well as ‘a right way’, that Qt5 specifically provides in the form of a Static Member Function of ‘QMessageBox’.

The problem with the wrong way is, that it not only allocates a message-box on the heap, but that in addition, the function call holds the only pointer to that object, which is named ‘box’ above. And, after the message box is ‘.show()’n, the function just quits, so that this only copy of the object’s address is also discarded. The object would never be destructed.

One thing which does not create an issue here, is how certain information was passed in to the function-calls. Either, that data was passed in by copy, or by reference, and on both cases, the existence of the ‘*message’ object is handled elsewhere, and what was passed in to the function-calls shown above, gets popped off the stack when those functions have finished running. The compiler sees to that. But according to the lines that have been commented out, the message-box object located at the address held by ‘box’ is never destructed. And therefore, if the user displays the About box many times, successive memory is allocated each time and not deallocated.

The message-box decides that it will not be shown any more, when the user clicks on its Accept button, but does not call a delete operation on itself. And, the calling function shown above has no idea, how long the user will take, before he decided to acknowledge the message. So, using the commented-out code, the function shown above also has no clear way to know when the ‘*box’ object will need to be deleted, if so.

Either way it gets displayed, the message box is modal, and greys out the application window, until it has been acknowledged.

The behaviour of the static method of ‘QMessageBox’ is such, that it does not return from being called, until the user has acknowledged the box, and when it returns, it has also deallocated whatever was allocated, be that on the stack or on the heap.

BTW, the version of the program which I have published to my blog, still uses the ‘leaky’ method.

FWIW, This Qt Forum Thread also responds to the question of my posting.

 

(Update 3/09/2020, 8h40: )

Most of the Qt5 ‘applications’ that I wrote, whose names begin with ‘Creator_Test…’, are only unimportant exercises presented for academic purposes. However, the application which is named ‘Dirk_Roots_GUI_1′ is one I’m serious about, because in some remote way it could provide a useful service, and so I repaired the problem that this posting describes, in that application. What I did was to override the following two event handlers as below:

 


void DialogWindow::closeEvent(QCloseEvent *event) {
    QWidget::closeEvent(event);
    delete this;
}

void MyPlot::closeEvent(QCloseEvent *event) {
    QMainWindow::closeEvent(event);
    delete this;
}

 

Each of the function implementations occurs in a different source file, and, because I’m overriding them, I must also declare each in a separate header file. But what my event handlers now do is, to allow the base-class of each of my derived classes, to handle the ‘Close Event’ first, which actually just causes the window no longer to appear on the screen. After the base-class is finished handling the events, my overridden handlers actually delete the window objects in question. And because both these window classes have working destructors, this should guarantee that the user may create as many of the windows as he chooses, close each one, and not  cause a memory leak by doing so. The patched files can be found here:

https://dirkmittler.homeip.net/binaries/

 


 

(Update 9/03/2020, 12h10: )

The fact has come to my attention that an alternative way exists, to solve the problem specifically, that widgets which are closed, may not call ‘delete’ on themselves. The Qt documentation describes this other solution. According to that, in my constructors for the two derived object classes, I could simply call the ‘setAttribute()‘ function, with ‘Qt::WA_DeleteOnClose‘ as its argument.

Which of these solutions is to be used, is a matter for the programmer to choose. What the programmer cannot do, is to apply both solutions. The reason for that is the fact that, in C++, to delete the same object twice is a fatal error. In fact, before smart-pointers became popular, if a situation existed where more than one object or function could work with the same pointer, and ‘delete’ was to be called on it, the advice which was given was, to set the pointer to ‘NULL’ immediately after carrying out the ‘delete’ operation, because to delete a NULL-pointer is always safe. Doing so causes no problems at all. Nothing happens.

Yet, it’s never allowed to assign a new value to the built-in ‘this’ pointer.

Having said that, I’d conclude that the best solution to this one form of memory leak in particular is, to set the ‘Qt::WA_DeleteOnClose‘ attribute. Hence, I have modified my published source code again, this time applying the more elegant solution.

Yet, the fact persists that any one application window will have scores of widgets and layout managers, that auxiliary windows will have dozens of them, etc. Thus, the overall fact remains in GUI programming that, unless the programmer is extremely careful, there is likely to be some memory leak.


 

(Update 9/03/2020, 14h20: )

I suppose that one argument which the reader might raise could be, that because message-boxes have a specialized role, they might already have their ‘Qt::WA_DeleteOnClose‘ attribute set. But then, just to be sure about that, I would suggest that the correct alternative on how to set up a complex message-box, would be something like so:

 


void MainWindow::showAbout() {
    QMessageBox *box = new QMessageBox(this);
    box->setAttribute(Qt::WA_DeleteOnClose);
    box->setTextFormat(Qt::RichText);
    box->setText(*message);
    box->show();
}

 

 

Dirk

 

Print Friendly, PDF & Email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>