QT父子与QT对象delete
原地址:http://www.qteverywhere.com/archives/437
很多C/C++初学者常犯的一个错误就是,使用malloc、new分配了一块内存却忘记释放,导致内存泄漏。Qt的对象模型提供了一种Qt对象之间的父 子关系,当很多个对象都按一定次序建立起来这种父子关系的时候,就组织成了一颗树。当delete一个父对象的时候,Qt的对象模型机制保证了会自动的把 它的所有子对象,以及孙对象,等等,全部delete,从而保证不会有内存泄漏的情况发生。
任何事情都有正反两面作用,这种机制看上去挺好,但是却会对很多Qt的初学者造成困扰,我经常给别人回答的问题是:1,new了一个Qt对象之后,在什么 情况下应该delete它?2,Qt的析构函数是不是有bug?3,为什么正常delete一个Qt对象却会产生segment fault?等等诸如此类的问题,这篇文章就是针对这个问题的详细解释。
在每一个Qt对象中,都有一个链表,这个链表保存有它所有子对象的指针。当创建一个新的Qt对象的时候,如果把另外一个Qt对象指定为这个对象的父对象, 那么父对象就会在它的子对象链表中加入这个子对象的指针。另外,对于任意一个Qt对象而言,在其生命周期的任何时候,都还可以通过setParent函数 重新设置它的父对象。当一个父对象在被delete的时候,它会自动的把它所有的子对象全部delete。当一个子对象在delete的时候,会把它自己 从它的父对象的子对象链表中删除。
QWidget是所有在屏幕上显示出来的界面对象的基类,它扩展了Qt对象的父子关系。一个Widget对象也就自然的成为其父Widget对象的子 Widget,并且显示在它的父Widget的坐标系统中。例如,一个对话框(dialog)上的按钮(button)应该是这个对话框的子 Widget。
关于Qt对象的new和delete,下面我们举例说明。
例如,下面这一段代码是正确的:
int main()
{
QObject* objParent = new QObject(NULL);
QObject* objChild = new QObject(objParent);
QObject* objChild2 = new QObject(objParent);
delete objParent;
}
我们用一张图来描述这三个对象之间的关系:

在上述代码片段中,objParent是objChild的父对象,在objParent对象中有一个子对象链表,这个链表中保存它所有子对象的指针,在 这里,就是保存了objChild和objChild2的指针。在代码的结束部分,就只有delete了一个对象objParent,在 objParent对象的析构函数会遍历它的子对象链表,并且把它所有的子对象(objChild和objChild2)一一删除。所以上面这段代码是安 全的,不会造成内存泄漏。
如果我们把上面这段代码改成这样,也是正确的:
int main()
{
QObject* objParent = new QObject(NULL);
QObject* objChild = new QObject(objParent);
QObject* objChild2 = new QObject(objParent);
delete objChild;
delete objParent;
}
在这段代码中,我们就只看一下和上一段代码不一样的地方,就是在delete objParent对象之前,先delete objChild对象。在delete objChild对象的时候,objChild对象会自动的把自己从objParent对象的子对象链表中删除,也就是说,在objChild对象被 delete完成之后,objParent对象就只有一个子对象(objChild2)了。然后在delete objParent对象的时候,会自动把objChild2对象也delete。所以,这段代码也是安全的。
Qt的这种设计对某些调试工具来说却是不友好的,比如valgrind。比如上面这段代码,valgrind工具在分析代码的时候,就会认为objChild2对象没有被正确的delete,从而会报告说,这段代码存在内存泄漏。哈哈,我们知道,这个报告是不对的。
我们在看一看这一段代码:
int main()
{
QWidget window;
QPushButton quit("Exit", &window);
}
在这段代码中,我们创建了两个widget对象,第一个是window,第二个是quit,他们都是Qt对象,因为QPushButton是从 QWidget派生出来的,而QWidget是从QObject派生出来的。这两个对象之间的关系是,window对象是quit对象的父对象,由于他们 都会被分配在栈(stack)上面,那么quit对象是不是会被析构两次呢?我们知道,在一个函数体内部声明的变量,在这个函数退出的时候就会被析构,那 么在这段代码中,window和quit两个对象在函数退出的时候析构函数都会被调用。那么,假设,如果是window的析构函数先被调用的话,它就会去 delete quit对象;然后quit的析构函数再次被调用,程序就出错了。事实情况不是这样的,C++标准规定,本地对象的析构函数的调用顺序与他们的构造顺序相 反。那么在这段代码中,这就是quit对象的析构函数一定会比window对象的析构函数先被调用,所以,在window对象析构的时候,quit对象已 经不存在了,不会被析构两次。
如果我们把代码改成这个样子,就会出错了,对照前面的解释,请你自己来分析一下吧。
int main()
{
QPushButton quit("Exit");
QWidget window;
quit.setParent(&window);
}
但是我们自己在写程序的时候,也必须重点注意一项,千万不要delete子对象两次,就像前面这段代码那样,程序肯定就crash了。
最后,让我们来结合Qt source code,来看看这parent/child关系是如何实现的。
在本专栏文章的第一部分“对象数据存储”,我们说到过,所有Qt对象的私有数据成员的基类是QObjectData类,这个类的定义如下:
typedef QList<QObject*> QObjectList;
class QObjectData
{
public:
QObject *parent;
QObjectList children;
// 忽略其它成员定义
};
我们可以看到,在这里定义了指向parent的指针,和保存子对象的列表。其实,把一个对象设置成另一个对象的父对象,无非就是在操作这两个数据。把子对 象中的这个parent变量设置为指向其父对象;而在父对象的children列表中加入子对象的指针。当然,我这里说的非常简单,在实际的代码中复杂的 多,包含有很多条件判断,有兴趣的朋友可以自己去读一下Qt的源代码。
http://www.cnblogs.com/foxhengxing/archive/2010/12/24/1916355.html
QT父子与QT对象delete的更多相关文章
- Qt Meta Object System-元对象系统
研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 元对象系统 ...
- 【Qt开发】Qt中图像的显示与基本操作
Qt可显示基本的图像类型,利用QImage.QPxmap类可以实现图像的显示,并且利用类中的方法可以实现图像的基本操作(缩放.旋转). 1. Qt可显示的图像类型 参考Qt的帮助文档,可支持的类型,即 ...
- 【Qt】关于Qt【转】
什么是Qt Qt是一个针对桌面.嵌入式.移动设备的一个跨平台的应用程序开发框架,支持的平台包括Linux.OS X.Windows.VxWorks.QNX.Android.iOS.BlackBerry ...
- Qt分析:Qt中的两种定时器(可是QObject为什么要提高定时器呢,没必要啊。。。)
Qt有两种定时器,一种是QObject类的定时器,另一种是QTimer类的定时器. (1)QObject类的定时器 QObject类提供了一个基本的定时器,通过函数startTimer()来启 ...
- Qt 外观之一 ——Qt Style Sheet
Qt Style Sheet 目录 使用 对于应用程序 创建自定义控件 QSS语法 一般选择器(selector) 伪选择器 解决冲突 使用specificity Namespace冲突 级联效应 设 ...
- Qt 绘制图表 - Qt Charts版
一.前言 自从 Qt 发布以来,给广大跨平台界面研发人员带来了无数的福利.但是Qt自己却一直没有提供自带的图表库,这就使得 QWT.QCustomPlot 等第三方图表库有了巨大的生存空间,为了降低开 ...
- 【Qt开发】QT中显示图像数据
一般图像数据都是以RGBRGBRGB--字节流的方式(解码完成后的原始图像流),我说成字节流,那就表明R,G,B的值各占一个字节,在编程时表示的就是unsigned char * data. 我们先来 ...
- Qt环境搭建(Qt Creator)+Visual Studio
1.http://www.cnblogs.com/ranjiewen/p/5318768.html 简述 经常有人问我编写Qt程序时使用什么IDE,其实这个真的很难回答(各有所长),只能说看个人爱好了 ...
- Qt Creator提示"Qt没有被正确安装,请运行make install"的解决办法
笔者最近使用Qt在开发一些小程序,觉得这个框架设计确实很好,使用了信号和槽解决了组件之间的通讯问题,可以说是基于C++语言上一个非常大的创新,大大提高了开发人员的编码效率,也使整个C++语言更加抽象. ...
随机推荐
- [原]bochs+dos6.22汇编环境
1.下载安装bochs 下载MS-DOS http://files.cnblogs.com/allbymyself/DOS6.22.rar 下载Masm5.0 2.bochs配置 1)安装目录下的bx ...
- Java 学习 第三篇;面向对象
1:Java的常用包: 核心类在java 包中:扩展类在javax包中 java.lang 系统默认自动导入 包含String Math System Thread等类 java.util 包含了工具 ...
- C#设置标记方法等为否决的不可用
C#如何标记类里面的方法或者类为否决的,不可使用.在VS IDE编辑器中使用此方法或者类时会用绿色的波浪线标记这个语句,当移动鼠标到这句代码上时,会出现[否决的]方法名,警告“方法名称”已过时: ...
- BZOJ 1101 Zap(莫比乌斯反演)
http://www.lydsy.com/JudgeOnline/problem.php?id=1101 给定a,b,d,求有多少gcd(x,y)==d(1<=x<=a&& ...
- \s+(?<request_time>\d+(?:\.\d+)?)\s+ 解释
<pre name="code" class="html"><pre name="code" class="ht ...
- 静态链表实现 (A-B)U(B-A)
图中黄色部分为(A-B)U(B-A)的实际意义,用结构数组做静态链表来实现该表达式 大致流程是先建立A链表,接着将挨个输入的B中元素在A链表中遍历.如果没找到,就加到A链表结尾下标为endpointe ...
- Vanya and Scales(思维)
Vanya and Scales time limit per test 1 second memory limit per test 256 megabytes input standard inp ...
- 最逼近Mac OS的Linux系统 -- Elementary OS
也不知道有多少人听说过Elementary OS?elementary Project 最开始只有一个 elementary 主题,之后延伸出了很多项目,形成了 elementary Project ...
- Linux转发性能评估与优化-转发瓶颈分析与解决方式(补遗)
补遗 关于网络接收的软中断负载均衡,已经有了成熟的方案,可是该方案并不特别适合数据包转发,它对server的小包处理非常好.这就是RPS.我针对RPS做了一个patch.提升了其转发效率. 下面是我转 ...
- Win8 使用VC6.0调试
Win8.1下无法执行vc++6.0的解决方法 注意 安装过程中最后一步会卡在那里不动,能够直接关闭安装程序,忽略报错. 1 安装完毕后在安装文件夹下找到MSDEV.EXE 而且将 MSDEV.EXE ...