(转载) Qt deletelater函数分析(2)

在C++中,deletenew 必须 配对使用,Qt作为C++的库,显然是不会违背C++原则。但是,qt有自己的内存管理,有时候虽然使用了new,却可以不用使用delete。

Qt半自动的内存管理:

在Qt中,以下情况下你new出的对象你可以不用亲自去delete:

  1. QObject及其派生类的对象,如果其parent非0,那么其parent析构时会析构该对象。

  2. 有些类的对象可以接收设置一些特别的标记,比如:

    QWidget及其派生类的对象,可以设置 Qt::WA_DeleteOnClose 标志位(当close时会析构该对象)

    QAbstractAnimation派生类的对象,可以设置 QAbstractAnimation::DeleteWhenStopped

    QRunnable::setAutoDelete()

    MediaSource::setAutoDelete()


在Qt中,最基础和核心的类是:QObject 。这里只关注两点:
父子关系deleteLater

1)父子关系

在Qt中,每个 QObject 内部都有一个list,用来保存所有的 children,还有一个指针,保存自己的parent。当它自己析构时,它会将自己从parent的列表中删除,并且析构掉所有的children。

注意: 这里讲的paent,child与c++的语法无关。这里:如果你在qml文件中写了一个Rectangle,在其内部又加了一个Text,则Text的parent是Rectangle。

建立与解除

创建一个QObject对象时,如果指定了父对象,它就会将自己添加到父对象的 children 列表中:

在这里插入图片描述

当一个QObject对象析构时,它会将自己从父对象的 children 列表中移除(parent非0的话):

官方还是建议通过deletelater来进行删除Qobject对象。

在这里插入图片描述

void QObject::setParent ( QObject * parent )

通过该函数,将自己从原父对象的children中删除,添加到新parent的children列表中:

在这里插入图片描述

获取父对象:QObject * QObject::parent () const

在这里插入图片描述

获取子对象,子对象可以有多个:const QObjectList & QObject::children () const

在这里插入图片描述

也可以通过findChild来查找某个child。

2)deleteLater

可以下载qt源码,看该函数的实现,我下载了qt5.11的源码:qt-everywhere-src-5.11.1, http://download.qt.io/official_releases/qt/5.11/5.11.1/single/

Qojbect.cpp(文件位置:qt-everywhere-src-5.11.1\qt-everywhere-src-5.11.1\qtbase\src\corelib\kernel )中对该函数的定义:

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

该函数的作用就是发出一个事件,请求自己被干掉。

事件循环稍后收到该事件,会将其派发给这个请求的发出者,然后,事件发出者调用delete将自己干掉:

bool QObject::event(QEvent *e)
{
    
    
    switch (e->type()) {
    
    
...
    case QEvent::DeferredDelete:
        qDeleteInEventHandler(this);
        break;}

void qDeleteInEventHandler(QObject *o)
{
    
    
    delete o;
}

例子1:

#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。所以,这儿会造成内存泄露。

三种改进方式

1) 分配对象到stack而不是heap中

QLabel label("Hello Qt!");
label.show();

2) 手动调用delete

int ret = app.exec();
delete label;
return ret;

3)设置标志位,这样,当我们点击关闭按钮时,close()函数将会调用deleteLater

label->setAttribute(Qt::WA_DeleteOnClose);

例子2

#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为对象分配空间时,反倒需要多多小心了。

例子3:这个程序退出时会直接崩溃 。

#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中,delte stack中的东西会导致崩溃。

两种改进办法:

1)将label分配到heap中

   QLabel *label = new QLabel("Hello Qt!");
   label.setParent(&w)

2)确保label先于其parent被析构(调整一下顺序),这样,label析构时将自己从父对象的列表中移除自己,w析构时,children列表中就不会有分配在stack中的对象了。

   QWidget w;
   QLabel label(tr"Hello Qt!");

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

参考:
https://blog.csdn.net/dbzhang800/article/details/6300025
http://doc.qt.nokia.com/4.7/qobject.html
http://www.cuteqt.com/blog/?p=824
————————————————

原文链接:https://blog.csdn.net/qq_35865125/article/details/86531953

猜你喜欢

转载自blog.csdn.net/u011942101/article/details/130110769