Qt中内存泄露和退出崩溃的问题 delete
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所引入的,与类的继承关系无关
建立与解除
QObject::~QObject () [virtual]
- 当一个QObject对象析构时,它会将自己从父对象的 children 列表中移除(parent非0的话)
QObjectPrivate::setParent_helper(QObject *o)
获取父、子对象
每个QObject只有一个父对象:
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++例子:
class A
{
public:
A(){}
void deleteMe()
{
delete this;
}
}; int main()
{
A * a = new A;
a->deleteMe();
return 0;
}
应该不需要解释吧
later
Qt 是事件驱动的,所以发送一个删除事件到事件系统就可以啦:
void QObject::deleteLater()
{
QCoreApplication::postEvent(this, new QEvent(QEvent::DeferredDelete));
}
事件循环稍后看到该事件就会将其派发会这个widget:
bool QObject::event(QEvent *e)
{
switch (e->type()) {
...
case QEvent::DeferredDelete:
...
一些例子
无关痛痒?
很简短、很熟悉的一个例子是不?但是 如果你发现对象的析构函数始终不被成功调用,会有什么感觉?
#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,所以,使用时还是要当心。
Qt中内存泄露和退出崩溃的问题 delete的更多相关文章
- Qt中内存泄露和退出崩溃的问题
http://blog.csdn.net/wangkuiyun/article/details/7412379
- java中内存泄露有几种?如何分析泄露原因
一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Hea ...
- go中内存泄露的发现与排查
一,什么是内存泄漏 Go 中的并发性是以 goroutine(独立活动)和 channel(用于通信)的形式实现的.处理 goroutine 时,程序员需要小心翼翼地避免泄露.如果最终永远堵塞在 I/ ...
- Java中内存泄露及垃圾回收机制
转自:http://blog.sina.com.cn/s/blog_538b279a0100098d.html 写的相当不错滴...................... 摘 要 Java语言中,内 ...
- Android中内存泄露与如何有效避免OOM总结
一.关于OOM与内存泄露的概念 我们在Android开发过程中经常会遇到OOM的错误,这是因为我们在APP中没有考虑dalvik虚拟机内存消耗的问题. 1.什么是OOM OOM:即OutOfMemoe ...
- Windows系统中内存泄露与检测工具及方法
1.检测需要使用的工具:windbg工具.检测前,需要先安装windbg工具.安装了该工具后,会在安装目录下有一个umdh工具.假设windbg安装在以下目录下:D:\Program Files\De ...
- java中内存泄露和内存溢出
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory:比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出. ...
- C++中内存泄露的检测
C++没有java的内存垃圾回收机制,在程序短的时候可能比较容易发现问题,在程序长的时候是否有什么检测的方法呢? 假设有一个函数可以某点检测程序的内存使用情况,那是否可以在程序开始的时候设置一个点,在 ...
- Qt中layout()->setSizeConstraint(QLayout::SetFixedSize);崩溃的问题
编译环境: win764位,vs2008编译器,cbd调试器,qt4.8 背景: 按照<C++ Gui Qt4编程>书中第二章的一个例子(sortDialog)一步步抄完,编译运行,显示不 ...
随机推荐
- appium关键字:
## Appium 服务关键字 <expand_table> |关键字|描述|实例||----|-----------|-------||`automationName`|你想使用的自动化 ...
- Python3中@的作用
可能是自己理解能力差,网上看了一大堆教程,完全没搞懂. 自己敲几行代码,终于理解是怎么回事了. #python 3.6 #!/usr/bin/env python # -*- coding:utf-8 ...
- Sublime Text 插件推荐——for web developers
楼主向高大上的: web front-end development engineer (好吧,google就是这样翻译的 ^_^)们推荐 ST 插件,在此抛砖引玉: NO.1 :Emmet (原名: ...
- 《Linux/UNIX系统编程手册》读书笔记
2018-1-30 一.UNIX.C语言以及Linux的历史回顾 1. UNIX简史.C语言的诞生 1969年,贝尔实验室的Ken Thompson首次实现了UNIX系统. 1973年,C语言步入成熟 ...
- CSS基础小记
2017/10/29 CSS 认识CSS样式 CSS全称为"层叠样式表 (Cascading Style Sheets)",它主要是用于定义HTML内容在浏览器内的显示样式,如文字 ...
- iOS- Exception异常处理
1.Exception 前言 在iOS里对异常的处理及捕获,并没有其它语言里那么常见,相信很多iOS程序员都知道,更多的时候是对内存的的检测与分析,检测相关内存方面的问题. 而在app闪退并不是因为内 ...
- ArrayList遍历(JAVA)
假如有个ArrayList变量如下: ArrayList<String> list = new ArrayList<String>(); list.add("arra ...
- 关于如何解决PHPCMS V9内容搜索显示不全问题解决方案
站长朋友们都晓得只要是开源的PHP程序都会有漏洞存在.如果想完美的建站就需要自己去研究打补丁了.最近很多站长联系小编咨询用phpcms建站当在首页搜索内容的时候有的居然搜索不到.小编感到很是奇怪于是就 ...
- QT分析之网络编程
原文地址:http://blog.163.com/net_worm/blog/static/127702419201002842553382/ 首先对Windows下的网络编程总结一下: 如果是服务器 ...
- zk分布锁的java实现
只做记录,直接上代码 父类: package com.ylcloud.common.lock; import com.alibaba.fastjson.JSON; import org.I0Itec. ...