Qt 半内存管理

在C++中学习过程当中,咱们都知道: delete 和 new 必须配对使用(一 一对应):delete少了,则内存泄露,多了麻烦更大。 Qt做为C++的库,显然是不会违背C++的前述原则的。但是: 在Qt中,咱们不少时候都疯狂地用new,却不多用delete,缺乏的 delete 去哪儿了?!app


注:本文暂不涉及智能指针(smart pointer)相关的东西,你能够考虑 Qt 智能指针学习 一文 Qt半自动的内存管理函数

在Qt中,如下状况下你new出的对象你能够不用亲自去delete (但你应该清楚delete在何处被Qt调用的,怎么被调用的):post

QObject及其派生类的对象,若是其parent非0,那么其parent析构时会析构该对象(本文内容围绕这一点展开 )学习

除此以外,有些类的对象能够接收设置一些特别的标记,好比:ui

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

QAbstractAnimation派生类的对象,能够设置spa

QAbstractAnimation::DeleteWhenStopped指针

QRunnable::setAutoDelete()code

MediaSource::setAutoDelete() ... 注意:这些用法会有些陷阱 ,请注意看本文最后的3个小例子。 在Qt中,最基础和核心的类是:QObject 。它的魔力很大,本文只关注两点: 父子关系 deleteLater对象

父子关系

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

注意:在 Qt 中,咱们常常会遇到基类、派生类,或父类、子类。

这是对于派生体系来讲的,和在C++相关书中看到的彻底同样,与这的parent无关父对象、子对象、父子关系。 这是Qt中所特有的,也就是这儿的parent所引入的,与类的继承关系无关 创建与解除

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Q_INVOKABLE QObject::QObject ( QObject * parent = 0 )
//建立一个QObject对象时,若是指定了父对象,它就会将本身添加到父对象的 children 列表中
QObject::~QObject () [ virtual ]
//当一个QObject对象析构时,它会将本身从父对象的 children 列表中移除(parent非0的话)
void QObject::setParent ( QObject * parent )
//经过该函数,将本身从原父对象的children中删除,添加到新parent的children列表中
//注:这三个函数都是经过一个内部私有函数来实现的,这就是
QObjectPrivate::setParent_helper(QObject *o)
//获取父、子对象
 
//每一个QObject只有一个父对象:
QObject * QObject::parent () const
//子对象能够有多个
const QObjectList & QObject::children () const
//因此能够根据条件来查找喽:
T QObject::findChild ( const QString & name = QString() ) const
QList<T> QObject::findChildren ( const QString & name = QString() ) const

deleteLater

deleteLater 包含两层意思了 delete later 呵呵,彷佛这是废话哈。 删除本身

在去年春节前的时候吧,有人对

obj-> deleteLater()

会像下面同样调用delete:

delete obj;

感到不解。而后我写了这样一个C++例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A
{
   public :
   A(){}
   void deleteMe()
   {
       delete this ;
   }
};
 
int main()
{
   A * a = new A;
   a->deleteMe();
   return 0;
}

应该不须要解释吧 later

Qt 是事件驱动的,因此发送一个删除事件到事件系统就能够啦:

?
1
2
3
4
void QObject::deleteLater()
{
     QCoreApplication::postEvent( this , new QEvent(QEvent::DeferredDelete));
}

事件循环稍后看到该事件就会将其派发会这个widget:

?
1
2
3
4
5
6
7
8
9
bool QObject::event(QEvent *e)
{
     switch (e->type())
     {
       case QEvent::DeferredDelete:
          ...
          break ;
     }
}

一些例子 无关痛痒? 很简短、很熟悉的一个例子是不?可是 若是你发现对象的析构函数始终不被成功调用 ,会有什么感受?

?
1
2
3
4
5
6
7
8
9
#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中

?
1
2
3
4
5
6
7
8
9
QLabel label( "Hello Qt!" );
label.show();
 
//设置标志位,这样,当咱们点击关闭按钮时,close()函数将会调用deleteLater
label->setAttribute(Qt::WA_DeleteOnClose);
//动手调用delete(不就是少了一个么,咱们补上还不行么)
int ret = app.exec();
delete label;
return ret;

单独列一个吧 强化一下对前一个例子的了解

?
1
2
3
4
5
6
7
8
9
10
#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为对象分配空间时,反倒须要多多当心了。 隐蔽很深?

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

?
1
2
3
4
5
6
7
8
9
10
#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中的对象会怎么样了。至关于

?
1
2
QLabel label();
delete &label;

两种改进办法: 一是,将label分配到heap中

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

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

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

Qt 对象的父子关系的引入,简化了咱们对内存的管理,可是,因为它会在你不太注意的地方调用 delete,因此,使用时仍是要小心。

相关文章
相关标签/搜索