Memory leaks and semi-automatic memory management in Qt



Qt helps programmers do some memory reclamation things, but because of these, people who are not familiar with it will make mistakes again and again.

Include a nice article:

In the process of learning in C++, we all know:

  • delete and new must be used in pairs (one-to-one correspondence): if there is less delete, the memory will leak, and more trouble will be more troublesome.

As a C++ library, Qt obviously will not violate the aforementioned principles of C++. But:

  • In Qt, we use new madly a lot of times, but rarely use delete, where is the missing delete? !

Note: This article does not cover things related to smart pointers, you can consider the  Qt smart pointer learning  article

Qt's semi-automatic memory management

In Qt, you don't need to delete the new object in the following cases (but you should know where and how delete is called by Qt):

  • For objects of QObject and its derived classes, if its parent is not 0, the object will be destructed when its parent is destructed (the content of this article revolves around this point )

In addition, objects of some classes can receive and set some special flags, such as:

  • Objects of QWidget and its derived classes can set the Qt::WA_DeleteOnClose flag (the object will be destructed when it is closed)
  • An object of a class derived from QAbstractAnimation, you can set QAbstractAnimation::DeleteWhenStopped
  • QRunnable::setAutoDelete()
  • MediaSource::setAutoDelete()
  • ...

Note: There are some pitfalls in these usages , please pay attention to the 3 small examples at the end of this article.

In Qt, the most basic and core class is: QObject. Its magic is great, and this article only focuses on two points:

  • father-son relationship
  • deleteLater

father-son relationship

In Qt, each QObject has a list inside it to save all children, and a pointer to save its own parent. When it destructs itself, it removes itself from the parent's list and destructs all children.

  • Note: In Qt, we often encounter
    • Base class, derived class, or parent class, subclass. This is for the derivation system, exactly as seen in C++ related books, regardless of the parent of this
    • Parent object, child object, parent-child relationship. This is unique to Qt, which is introduced by parent here, and has nothing to do with class inheritance

create and dismantle

Q_INVOKABLE QObject::QObject ( QObject * parent = 0 )
  • When a QObject is created, if a parent is specified, it adds itself to the parent's children list
QObject::~QObject () [virtual]
  • When a QObject object is destructed, it removes itself from the parent's children list (if parent is not 0)
void QObject::setParent ( QObject * parent )
  • Through this function, delete yourself from the children of the original parent object and add it to the list of children of the new parent

Note: These three functions are implemented through an internal private function, which is

QObjectPrivate::setParent_helper(QObject *o)

Get parent and child objects

Each QObject has only one parent:

QObject * QObject::parent () const

Child objects can have multiple

const QObjectList & QObject::children () const

So you can find it according to the conditions:

T QObject::findChild ( const QString & name = QString() ) const
QList<T> QObject::findChildren ( const QString & name = QString() ) const

deleteLater

deleteLater contains two meanings

  • delete
  • later

Ha ha, it seems this is nonsense.

delete yourself

Before the Spring Festival last year, someone was right

obj-> deleteLater()

delete will be called like this:

delete obj;

puzzled. Then I wrote a C++ example like this:

class A
{
  public:
  A(){}
  void deleteMe()
  {
      delete this;
  }
};

intmain()
{
  A * a = new A;
  a->deleteMe();
  return 0;
}

Shouldn't need an explanation

later

Qt is event driven, so sending a delete event to the event system is fine:

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

The event loop later sees the event and dispatches it to this widget:

bool QObject::event(QEvent *e)
{
    switch (e->type()) {
...
    case QEvent::DeferredDelete:
         ...

some examples

It doesn't matter?

A very brief, familiar example, isn't it? But how would you feel if you found that an object's destructor was never successfully called ?

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

这是 C++ GUI Programming with Qt 4 一书的第一个例子。我们注意到这儿的 label 既没有指定parent,也没有对其调用delete。

所以,这儿会造成内存泄露。

书中解释说,对于这种小例子,这点内存泄露不算什么。不清楚官方这个例子的意图是什么,或许是一开始就让大家用指针吧。

三种改进方式

  • 分配对象到stack而不是heap中
QLabel label("Hello Qt!");
label.show();
  • 设置标志位,这样,当我们点击关闭按钮时,close()函数将会调用deleteLater
label->setAttribute(Qt::WA_DeleteOnClose);
  • 动手调用delete(不就是少了一个么,我们补上还不行么)
int ret = app.exec();
delete label;
return ret;

单独列一个吧

强化一下对前一个例子的了解

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

运行正常,退出时会崩溃,因为label被close时,将会 delete 这儿label对象,但label对象却不是通过new分配到heap中的。

为了使得用户减少自己显式使用delete,Qt将delete隐藏的比较深。这样一来,不使用new为对象分配空间时,反倒需要多多小心了。

隐蔽很深?

看个小例子:这个程序退出时会直接崩溃

#include <QtGui>
int main(int argc, char* argv[])
{
   QApplication app(argc, argv);
   QLabel label(tr"Hello Qt!");
   QWidget w;
   label.setParent(&w);
   w.show();
   return app.exec();
}
  • 问题出在哪儿呢?因为退出时,w 比 label 先被析构,当 w 被析构时,会删除chilren列表中的对象,也就是这儿的 label。但 label 却不是通过new分配在heap中,而是在stack中,可想而知,delete 一个再stack中的对象会怎么样了。相当于
QLabel label();
delete &label;
  • 两种改进办法:
    • 一是,将label分配到heap中
   QLabel *label = new QLabel("Hello Qt!");
   label.setParent(&w)
  • 再一种就是,确保label先于其parent被析构(调整一下顺序),这样,label析构时将自己从父对象的列表中移除自己,w析构时,children列表中就不会有分配在stack中的对象了。
   QWidget w;
   QLabel label(tr"Hello Qt!");

Qt 对象的父子关系的引入,简化了我们对内存的管理,但是,由于它会在你不太注意的地方调用 delete,所以,使用时还是要当心。

原文链接:http://blog.csdn.net/dbzhang800/article/details/6300025

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325904553&siteId=291194637