Qt对象树原理_8注意2缺陷1迷惑_析构调用顺序

一、前言

在这里插入图片描述
可以看到上述代码中,new的对象没有手动释放,其原理就是对象树。

二、什么是对象树

下图是一个对象树,win是窗口,Topic、obj是小控件。

在这里插入图片描述

三、8注意

(1)

不是继承关系中的子类父类。这里的措辞是对象,指对象与对象之间的树状关系,是父对象与子对象的关系。

(2)

一个父对象可以包含多个子对象,但一个子对象只有一个父对象(原理:根据下图Qt帮助文档可知)

在这里插入图片描述

(3)

指定父对象(如btn->setParent(this),创建的这个QObject对象btn自动添加到父对象的children()列表)后,子对象就不需要手动释放。未指定的需要手动释放,否则出现内存泄露。

(4)

当父对象析构时,其子对象列表children()中所有的对象被析构。当子对象先析构时,子对象自动从父对象的children()列表删除。

堆区:首先delete掉对应对象空间,其子对象按顺序调用析构函数,父对象析构函数不会再调用,如下图

在这里插入图片描述

栈区:不允许delete

(5)

析构≠释放。

这里要和C++区分:在C++中,不存在继承关系时,按照创建顺序,先创建的先构造,先构造的后析构,如下图

在这里插入图片描述

存在继承关系时,父类先构造,后析构,如下图

在这里插入图片描述

在Qt中,不存在对象树时:构造析构顺序和C++不存在继承关系时一样,按照创建顺序,先创建的先构造,先构造的后析构。
①若在堆开辟空间,则先创建的先构造先析构,唯一要注意的就是销毁过程相反(即执行某个QObject对象的析构函数时还没有释放内存,在析构函数结束时才释放,这里常常产生错觉);
②若在栈开辟空间,则先创建后释放。
堆区,如下图:

在这里插入图片描述

栈区,如下图:

在这里插入图片描述

(6)

正常情况(不使用delete、无父对象子对象创建顺序问题)栈区内存释放过程:先创建后析构。如下图

在这里插入图片描述

(7)

正常情况堆区内存释放过程:用下图案例说明,从根节点开始(即最顶层父对象w1),程序中w1.show()显示窗口(w2、w3、w4包含在里面),这时候点击关闭按钮触发w1窗口的close()槽,进入w1的析构函数,首先执行w1析构函数内的qDebug()宏,输出信息“w1析构”(注意这时候w1还没有被释放),接着查看w1是否有子对象,发现有子对象w2,则继续进入w2的析构函数,输出信息“w2析构”,同理,进入w3析构函数,输出信息“w3析构”,最后进入w4的析构函数,输出信息“w4析构”,发现w4没有子对象,开始回走,首先结束w4析构函数,这时候才释放w4内存空间,接着释放w3,…,直到释放完全。如下图

在这里插入图片描述

(8)

QWidget是QObject的子类,在parent机制上无区别,但在实际使用时QWidget更复杂,原因是QWidget和QEventLoop高度配合才能完成工作。实际中,QWidget的关闭流程,首先用户点击关闭按钮触发close()槽,然后Qt向widget发送QCloseEvent,默认的QCloseEvent(用户没有重写,使用默认)将widget隐藏起来,即hide()。所以,通过QWidget关闭流程可知,Widget关闭的实质是隐藏,而没有释放内存。因此,需要设置Qt::WA_DeleteOnClose属性,使得close后调用widget的析构函数,另外一种就是直接手动delete。

四、2缺陷

(1)

上面说到先构造后析构(C++原理),即先创建后析构。有一种情况,先创建子对象,后创建父对象。

对于在栈区开辟空间的情况,根据第一句所说则应该先析构父对象,又遵从Qt对象树原理,则其子对象随之被析构,代码继续执行,按照第一句所说则会再析构一次子对象,这时出现对同一对象调用两次析构函数的情况,而C++中不允许重复调用两次析构函数,因此程序崩溃。如下图,关闭w1后程序crush

在这里插入图片描述

对于在堆区开辟空间的情况:子对象和父对象的创建先后无影响

在这里插入图片描述

(2)

由注意的(4)可知delete很危险。

五、析构顺序总结

(1)使用delete

堆区:delete的对象及其所有子对象,按顺序从父对象到子对象调用析构函数。(与创建顺序无关)
栈区:不允许delete。

(2)先创建子对象,后创建父对象

堆区:先创建先析构,但最后释放内存,也就是先析构子对象。
栈区:不允许,应用程序crush。

(3)正常情况(不使用delete、无父对象子对象创建顺序问题)

堆区:先析构父对象,但子对象先释放内存。
栈区:先创建后析构,也可以理解为先析构子对象后析构父对象,析构的同时释放内存。

六、1迷惑

不要把Qt和C++完全等比,正常情况下在堆区开辟的对象,其析构函数被调用时不会释放对象空间,即先创建的先调用析构函数,但是最后释放(等调用到最底层子对象结束后开始往回走,一个一个结束析构函数,这时候才真正的释放内存)。

七、针对上述,我们可以

①先创建父对象后创建子对象
②在堆上创建对象
③不要对指定了父对象的对象delete

八、文档

后续补上。

转载注明出处
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值