Talking about memory leaks in Qt

1. Introduction

Qt memory management mechanism: Qt can maintain the hierarchy of objects internally. For visual elements, this hierarchy is the relationship between child components and parent components; for non-visual elements, it is the affiliation relationship between one object and another object. In Qt, in Qt, deleting a parent deletes its children along with it.
In C++, delete and new must be used in pairs (one-to-one correspondence): if there are fewer deletes, memory leaks, and if there are too many, the trouble will be even greater. Qt uses new but rarely deletes, because the QObject class and its inherited classes set the parent (you can also use the setParent function or the parent's addChild during construction), so when the parent is deleted, all children related to the parent will be Automatically delete, without manual processing by the user. But the parent does not distinguish whether its child is new or allocated on the stack. This reflects the power of delete, which can release any object, but deleting objects on the stack will cause memory errors, which requires understanding Qt's semi-automatic memory management. Another problem: child does not know whether it has been deleted, so wild pointers may appear. Then you need to understand Qt's smart pointer QPointer.
In Qt, the most basic and core class is: QObject. There is a list inside QObject, which will save children, and a pointer to save parent. When it is destructed, it will delete itself from the parent list and destruct all children. .

Two, detailed explanation

2.1 Qt's semi-automatic memory management

(1) For objects of QObject and its derived classes, if its parent is not 0, the object will be destructed when its parent is destructed.
(2) For objects of QWidget and its derived classes, the Qt::WA_DeleteOnClose flag can be set (the object will be destroyed when it is closed).
(3) For objects of QAbstractAnimation derived classes, QAbstractAnimation::DeleteWhenStopped can be set.
(4) QRunnable::setAutoDelete(), MediaSource::setAutoDelete().
(5) Parent-child relationship: parent object, child object, parent-child relationship. This is unique to Qt, and has nothing to do with the inheritance relationship of the class. Passing parameters is related to the parent (base class, derived class, or parent class, subclass, which is for the derived system and has nothing to do with the parent).

2.2 Examples of memory problems

example one

#include <QApplication>
#include <QLabel>
 
int main(int argc, char *argv[])
{
    
    
    QApplication a(argc, argv);
    QLabel *label = new QLabel("Hello Qt!");
    label->show();
    return a.exec();
}

Analysis: The label neither specifies the parent nor calls delete on it, so it will cause a memory leak. This small example in the book will also have the problem of pointer memory.

Ways to improve: (1) Allocate objects on the stack instead of the heap.

#include <QApplication>
#include <QLabel>
 
int main(int argc, char *argv[])
{
    
    
    QApplication a(argc, argv);
    
    QLabel label("Hello Qt!");
    label.show();
    
    return a.exec();
}

2) Set the flag bit, and the label will be deleted after close().

label->setAttribute(Qt::WA_DeleteOnClose);

(3) Manually delete after new.

#include <QApplication>
#include <QLabel>
 
int main(int argc, char *argv[])
{
    
    
    QApplication a(argc, argv);
 
    int ret = 0;
    QLabel *label = new QLabel("Hello Qt!");
    label->show();
    ret = a.exec();
    delete label;
    return ret;
}

Example two

#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
    
    
    QApplication app(argc, argv);
    
    QLabel label("Hello Qt!");
    label.show();
    label.setAttribute(Qt::WA_DeleteOnClose);
    
    return app.exec();
}

Analysis: The program crashes, because when the label is closed, delete &label; but the label object is a memory space allocated on the stack, and the address on the delete stack will be wrong.

Some friends understand that the label is deleted twice and it is wrong. You can test QLabel label("Hello Qt!"); label.show();delete &label; the first delete will make an error.
Example three

#include <QApplication>
#include <QLabel>
int main(int argc, char* argv[])
{
    
    
   QApplication app(argc, argv);
    
   QLabel label("Hello Qt!");
   QWidget w;
   label.setParent(&w);
   w.show();
    
   return app.exec();
}

Analysis: There is a list inside the Object, which will save the children, and a pointer to save the parent. When it is destructed, it will delete it from the parent list and destruct all the children.
w is destructed before label, when w is destructed, the object label in the chilren list will be deleted, but label is allocated on the stack, and an error occurs because of deleting the object on the stack.
Improvement methods: (1) Adjust the order to ensure that the label is destroyed before its parent. When the label is destroyed, it will remove itself from the list of the parent object. When w is destroyed, there will be no allocation in the children list The objects in the stack are gone.

#include <QApplication>
#include <QLabel>
int main(int argc, char* argv[])
{
    
    
   QApplication app(argc, argv);
    
   QWidget w;
   QLabel label("Hello Qt!");
   label.setParent(&w);
   w.show();
    
   return app.exec();
}

(2) Assign the label to the heap.

QLabel *label = new QLabel("Hello Qt!");
label->setParent(&w);
// 或者
QLabel *label = new QLabel("Hello Qt!",this);

Example 4: wild pointer

#include <QApplication>
#include <QLabel>
int main(int argc, char* argv[])
{
    
    
   QApplication app(argc, argv);
    
   QWidget *w = new QWidget;
   QLabel *label = new QLabel("Hello Qt!");
   label->setParent(w);
   w->show();
   delete w;
   label->setText("go"); // 野指针
    
   return app.exec();
}

Analysis: The program ends abnormally. When deleting w, the label will be deleted, and the label becomes a wild pointer. Calling label->setText(“go”); makes an error.
Improvement method : QPointer smart pointer

Example 5: deleteLater
When a QObject is receiving the event queue, if you destroy it halfway, there is a problem, so please don’t directly delete a QObject in Qt. If you must do this, use QObject’s deleteLater() Function, it will clear the memory immediately after all events are sent and everything is processed, and there will be no problem even if deletelater is called multiple times.

Send a delete event to the event system:

void QObject::deleteLater()
{
    
    
    QCoreApplication::postEvent(this, new QEvent(QEvent::DeferredDelete));
}

Guess you like

Origin blog.csdn.net/houxian1103/article/details/129599929