原地址: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的更多相关文章

  1. Qt Meta Object System-元对象系统

    研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 元对象系统 ...

  2. 【Qt开发】Qt中图像的显示与基本操作

    Qt可显示基本的图像类型,利用QImage.QPxmap类可以实现图像的显示,并且利用类中的方法可以实现图像的基本操作(缩放.旋转). 1. Qt可显示的图像类型 参考Qt的帮助文档,可支持的类型,即 ...

  3. 【Qt】关于Qt【转】

    什么是Qt Qt是一个针对桌面.嵌入式.移动设备的一个跨平台的应用程序开发框架,支持的平台包括Linux.OS X.Windows.VxWorks.QNX.Android.iOS.BlackBerry ...

  4. Qt分析:Qt中的两种定时器(可是QObject为什么要提高定时器呢,没必要啊。。。)

    Qt有两种定时器,一种是QObject类的定时器,另一种是QTimer类的定时器.   (1)QObject类的定时器   QObject类提供了一个基本的定时器,通过函数startTimer()来启 ...

  5. Qt 外观之一 ——Qt Style Sheet

    Qt Style Sheet 目录 使用 对于应用程序 创建自定义控件 QSS语法 一般选择器(selector) 伪选择器 解决冲突 使用specificity Namespace冲突 级联效应 设 ...

  6. Qt 绘制图表 - Qt Charts版

    一.前言 自从 Qt 发布以来,给广大跨平台界面研发人员带来了无数的福利.但是Qt自己却一直没有提供自带的图表库,这就使得 QWT.QCustomPlot 等第三方图表库有了巨大的生存空间,为了降低开 ...

  7. 【Qt开发】QT中显示图像数据

    一般图像数据都是以RGBRGBRGB--字节流的方式(解码完成后的原始图像流),我说成字节流,那就表明R,G,B的值各占一个字节,在编程时表示的就是unsigned char * data. 我们先来 ...

  8. Qt环境搭建(Qt Creator)+Visual Studio

    1.http://www.cnblogs.com/ranjiewen/p/5318768.html 简述 经常有人问我编写Qt程序时使用什么IDE,其实这个真的很难回答(各有所长),只能说看个人爱好了 ...

  9. Qt Creator提示"Qt没有被正确安装,请运行make install"的解决办法

    笔者最近使用Qt在开发一些小程序,觉得这个框架设计确实很好,使用了信号和槽解决了组件之间的通讯问题,可以说是基于C++语言上一个非常大的创新,大大提高了开发人员的编码效率,也使整个C++语言更加抽象. ...

随机推荐

  1. JS扩展方法

    JS扩展方法与C#的扩展方法非常相似,也是可以链式调用的,也是通过对某个类的扩展写法来实现.这个东西非常好用,如果将预先写好的方法放到一个js里面引用的话,那么后面写js将非常有趣. 下面给出一个例子 ...

  2. Android APK安装包瘦身[转]

    很显然,APK安装包越小越好.下面从代码,资源文件,使用策略几个方面简要介绍下: 代码 保持良好的编程习惯,不要重复或者不用的代码,谨慎添加libs,移除使用不到的libs. 使用proguard混淆 ...

  3. Binary Tree Level Order Traversal II 解答

    Question Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, ...

  4. LeeCode-Remove Duplicates from Sorted List

    Given a sorted linked list, delete all duplicates such that each element appear only once. For examp ...

  5. poj 2411 新写法

    别以为我在刷水题.... 今天做了场srm,500pt想到了是dp但是无从下手,但是看了rng_58的神代码后顿觉海阔天空啊(盯着看了一个下午),相比于一年前的写法,真的是不忍直视啊, TC真是个好地 ...

  6. DataReader、Table、DataSet和Entity相互转化

    public class CommonService { #region DataReader转化 /// <summary> /// 将DataReader转化为Table /// &l ...

  7. oracle 库文件解决的方法 bad ELF interpreter: No such file or directory

    今天是2014-05-27,今天遇到一个lib问题,再次记录一下.这是一个案例,更是一种解决该问题的方法过程. 当我们在使用sqlplus 登陆unix数据库的时候,有可能出现类似:xxxxxx ba ...

  8. 兼容各个浏览器的H.264播放: H.264+HTML5+FLOWPLAYER+WOWZA+RMTP

    一.方案确定 计划做视频播放,要求可以播放H264编码的mp4文件,各个浏览器,各种终端都能播放. 首先查找可行性方案, http://www.cnblogs.com/sink_cup/archive ...

  9. mysql、sqlServer、hsql、oracle、db2各数据库支持的字段类型与最大精度

  10. Ubuntu 系统搭建php服务器 用ssh 远程操作

    一:在桌面下载xshell客户端连接 ,vmavar 上的 Ubuntu系统,遇到的问题跟大家分享一下,希望大家少走弯路 Ubuntu系统默认没有ssh server 要安装 apt-get inst ...