Qt 半内存管理

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

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

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

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

除此之外,有些类的对象可以接收设置一些特别的标记,比如:

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

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

QAbstractAnimation::DeleteWhenStopped

QRunnable::setAutoDelete()

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,所以,使用时还是要当心。

时间: 2024-11-09 00:13:50

Qt 半内存管理的相关文章

Qt的内存管理

在QT的程序中经常会看到只有new而不delete的情况,其实是因为QT有一套回收内存的机制,主要的规则如下: 1.所有继承自QOBJECT类的类,如果在new的时候指定了父亲,那么它的清理时在父亲被delete的时候delete的,所以如果一个程序中,所有的QOBJECT类都指定了父亲,那么他们是会一级级的在最上面的父亲清理时被清理,而不用自己清理: 2.程序通常最上层会有一个根的QOBJECT,就是放在setCentralWidget()中的那个QOBJECT,这个QOBJECT在 new的

关于Qt半自动内存管理的思考及实验

一时兴起,对Qt感了兴趣,决心想要研究一下. 按网上资料配好环境,Windows 7 64bit + Qt 5.3.1 + VS2010. 根据<C++ GUI Qt4 编程>这本书,写出了第一个程序HelloQt,程序如下: #include <QApplication> #include <QLabel> #if _MSC_VER >= 1600 #pragma execution_character_set("utf-8") #endif

Qt的内存管理机制

当我们在使用Qt时不可避免得需要接触到内存的分配和使用,即使是在使用Python,Golang这种带有自动垃圾回收器(GC)的语言时我们仍然需要对Qt的内存管理机制有所了解,以更加清楚的认识Qt对象的生命周期并在适当的时机加以控制或者避免进入陷阱. 这篇文章里我们将学习QObject & parent对象管理机制,以及QWidget与内存管理这两点Qt的基础知识. QObject和内存管理 在Qt中,我们可以大致把对象分为两类,一类是QObject和它的派生类:另一类则是普通的C++类. 对于第

Qt 内存管理机制

这篇文章首先发布于我的主页 http://www.devbean.info,以后也会直接发布在那里.现在有 Flex 4 的一篇和 <从 C++ 到 Objective-C>系列,感谢大家支持! 强类型语言在创建对象时总会显式或隐式地包含对象的类型信息.也就是说,强类型语言在分配对象内存空间时,总会关联上对象的类型.相比之下,弱类型 语言则不会这样做.在分配了内存空间之后,有两种方法释放空间:手工释放,或者是使用垃圾收集器.C++ 要求开发者手工释放内存空间.这样做的好处是,开发者对内存有完全

6.关于QT中的内存管理,动态的制作,动态库的调用,静态库的制作

 一  QT的内存管理 1  QT中的内存管理是QObject来管理的 2  QT中的内存管理没有cocos2dx中的引用计数 3  组件可以指定父对象 QTimer *timer = QTimer(this);   //这里的this实际上强制转换成为了QObject了. 4  每一个对象都有一个deleteLater()方法, QLineEdit* edit = new QLineEdit(this); delete edit;   //这里的delete马上调用析构函数对内存进行释放

Java内存管理和垃圾回收

Java运行时内存区域 程序计数器,线程独占,当前线程所执行的字节码的行号指示器,每个线程需要记录下执行到哪儿了,下次调度的时候可以继续执行,这个区是唯一不会发生oom的 栈,线程独占,包含虚拟机栈或native method stack,用于存放局部变量的 堆,线程共享,用于分布对象实例的,后面说的内存管理和垃圾回收基本都是针对堆的 方法区,线程共享,用于存放被虚拟机加载的类,常量,静态变量; Java虚拟机规范,把方法区描述为堆的逻辑部分,所以也被称为“永久代”,在大量使用反射,动态代理,C

Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收

很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确实很低,一方面,Java语言采用面向对象思想,这也决定了其必然是开发效率高,执行效率低.另一方面,Java语言对程序员做了一个美好的承诺:程序员无需去管理内存,因为JVM有垃圾回收(GC),会去自动进行垃圾回收. 其实不然: 1.垃圾回收并不会按照程序员的要求,随时进行GC. 2.垃圾回收并不会及时

AS3的垃圾回收与内存管理

GC 和内存泄露无关 垃圾回收,这次是一个被无数人讨论过的传统话题. Action Script 使用的是和 Java 相似的内存管理机制,并不会即时回收废弃对象的内存,而是在特定时间统一执行一次 GC(Gabage Collection)操作来释放废弃对象的内存,避免了重复判断是否需要回收产生的性能问题. 但要注意,这只是决定回收的时机,而不是回收的内容.这个延迟执行内存回收也就是个表面的现象,不管什么时候执行 GC,能够回收的内存最终都能回收,不能回收的肯定不能回收.唯一的影响是,因为回收是

操作系统原理(二)——内存管理之页面置换算法

页面置换算法 1. 总述 为提高内存利用率,解决内存供不应求的问题,更加合理的使用内存,人们创造了分页式内存抽象.同时有一个虚拟内存的概念,是指将内存中暂时不需要的部分写入硬盘,看上去硬盘扩展了内存的容量,所以叫做“虚拟”内存.使用虚拟内存,应用程序可以使用比实际物理内存更大的内存空间.可以认为这个更大的内存空间就在硬盘上,只有将某一部分需要被用到时,才被写入真实内存:当它暂时不再被用到时,又被写回硬盘.分页式内存管理将物理内存分为等大的小块,每块大小通常为1K.2K.4K等,称为页帧:逻辑内存