从 Qt 的 delete 说开来
原地址:http://blog.csdn.net/dbzhang800/article/details/6300025
在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所引入的,与类的继承关系无关
建立与解除
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++例子:
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父子与QT对象delete
原地址:http://www.qteverywhere.com/archives/437 很多C/C++初学者常犯的一个错误就是,使用malloc.new分配了一块内存却忘记释放,导致内存泄漏.Qt的 ...
- Qt中delete的问题
最近项目遇到了一个bug,压力测试ui总会崩溃,gdb调试未果,跑到了库函数,无从查起: (gdb)bt #0 0x4146b1e4 in QWidgetPrivate::drawWidget(QPa ...
- Qt 智能指针学习(7种指针)
Qt 智能指针学习 转载自:http://blog.csdn.net/dbzhang800/article/details/6403285 从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ ...
- Qt 之 show,hide,setVisible,setHidden,close 等小结
0QObject::deleteLater()delete obj;析构对象1QWidget::setVisible(bool)使得Widget可见或不可见2QWidget::setHidden(bo ...
- Qt 智能指针学习(7种QT的特有指针)
从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...
- Qt 智能指针学习(7种QT智能指针和4种std智能指针)
从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...
- Qt 智能指针学习
原地址:http://blog.csdn.net/dbzhang800/article/details/6403285 从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include & ...
- [转]Qt 智能指针学习
从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...
- qt delete
在写博文之前,先推荐一篇文章,我觉着挺不错,也是qt中delete相关:QT父子与QT对象delete 学习C++的童鞋都知道new和delete必须是同时出现的,否则就会导致内存泄露 ...
随机推荐
- Js动态设置Img大小
function ResizePic() { $('img').each(function () { var maxWidth = 450; // 图片最大宽度 ...
- 关于left join 和 inner join
今天遇到一个逻辑很复杂的SQL,虽然写出来了,但是并没有完全体会,找了找资料,算是摸清楚了left join和inner join 的实际意义. 感谢PCJIM的文章,写的非常明白,原文地址:http ...
- 学习MVC遇到的问题
修改电脑上的DNS配置: Opendns 首选DNS服务器和备用DNS服务器分别设置为208.67.222.222和208.67.220.220 google的8.8.8.8 首选DNS服务器和备用D ...
- BZOJ 1612: [Usaco2008 Jan]Cow Contest奶牛的比赛( floyd )
对于第 i 头牛 , 假如排名比它高和低的数位 n - 1 , 那么他的 rank 便可以确定 . floyd -------------------------------------------- ...
- SQLSERVER 跨服务器查询
SELECT * FROM OPENDATASOURCE( 'SQLOLEDB', 'Data Source=IP;User ID=UserId;Password=Pa ...
- js中去除换行(\r\n)
解决方法:replace(/\r\n/g,"").replace("\n","") 测试: <script> var str = ...
- codeforces 464B Restore Cube
题目链接 给8个点, 判断这8个点能否组成一个正方体, 如果能, 输出这8个点. 同一个点的x, y, z可以交换. 每一个点有6种排列方式, 一个8个点, 暴力枚举出所有排列方式然后判断能否组成正方 ...
- mysql merge table
SELECT COUNT(*) FROM `comment` SHOW CREATE TABLE `comment` CREATE TABLE `comment1` ( `id` INT(8) NOT ...
- kinect for windows - 环境搭建
我是在虚拟机上搭建的开发环境,需要准备如下软件: 1)vmware workstation 10.0.2 (可以去官网下载,key就自己百度吧) 2)win7 32位(一定是32位的) 3)vs201 ...
- Apache BeanUtils 1.9.2 官方入门文档
为什么需要Apache BeanUtils? Apache BeanUtils 是 Apache开源软件组织下面的一个项目,被广泛使用于Spring.Struts.Hibernate等框架,有数千个j ...