Qt对象树

Qt中QObject类是所有Qt对象的基类。

Qt通过对象树管理QObject类及其子类。

创建QObject对象时(不论是一在个堆上还是栈上),可以指定一个父对象,父对象会将这个新的子对象添加到children列表中(可以用findchild和findchildren查看)。当父对象析构时,会将children列表中的对象析构(但是两者并没有明确的先后顺序),从而在一定程度上简化了内存管理。

下面看一个例子。myButton是继承自QPushButton的一个子类,并对其析构函数修改,添加打印信息。

#include "mybutton.h"myButton::myButton(QWidget *parent) : QPushButton(parent)
{setText("myButton");
}myButton::~myButton()
{qDebug() << "delete myButton";
}

helloworld继承自QWidget,在helloworld中new一个myButton对象,并不在析构函数中delete它,在析构函数修改,添加打印信息。

#include "helloworld.h"helloworld::helloworld(QWidget *parent): QWidget(parent)
{resize(800,600);myButton *btn = new myButton(this);
}helloworld::~helloworld()
{qDebug() << "delete helloworld";
}

运行后如下,将主窗体关闭后查看后台信息。

我们并没有对new出来的btn做delete,但是同样被析构了,这就是Qt对象树的作用。


问题一:

而我们如果把main.cpp中的

 helloworld w;

改为

helloworld *w = new helloworld;

运行后关闭主窗口,在后台没有析构的打印信息。

这是因为,对于在对上面创建的w,如果没有delete,则关闭按钮只是将窗体隐藏。这个对象仍然存在,可以重新调用show再次显示。

#include "helloworld.h"
#include <QApplication>         int main(int argc, char *argv[])
{QApplication a(argc, argv);//堆上验证对象树helloworld *w = new helloworld();w->show();w->close();Sleep(1000);w->show();return a.exec();
}

上述代码的效果是主窗体显示1s后,关闭,1s后重新打开。(Sleep需要添加Windows.h头文件)

我们可以做个实验:在helloworld.cpp中不断创建局部变量(栈上)。myWidget是继承QWidget的子类。

    myButton *btn=new myButton(this);connect(btn,&myButton::clicked,[=](){//在堆上创建    myWidget *widget = new myWidget();widget->resize(300,200);myButton *btn = new myButton(widget);widget->show();widget->close();   });int i=10000;while (i--) {btn->click();}

这段代码就是运行后不断点击按钮btn在栈上创建一个widget和button,并关闭widget。运行程序后打开任务管理器,查看内存使用情况,会随着btn按钮调用,内存在不断增加(内存泄漏),且后台没有调用myWidget和myButton的析构函数打印信息。

而如果换成如下代码:

    myButton *btn=new myButton(this);connect(btn,&myButton::clicked,[=](){//在栈上创建    myWidget widget;widget.resize(300,200);myButton *btn = new myButton(&widget);widget.show();widget.close();  });int i=10000;while (i--) {btn->click();}

则内存不会增加,且每次widget.close();都会在后台打印析构信息。

同样道理,对于创建全局变量,如果创建在堆上,则应该指定父对象,或者在调用类的析构函数中delete实例化对象,或者设置

Qt::WA_DeleteOnClose属性,在窗口关闭后主动delete对象。


问题二:

对于在栈上创建的对象。

对于局部变量

    //栈上创建对象的对象树问题myWidget widget;widget.resize(300,200);myButton btn;btn.setParent(&widget);widget.show();

运行后会一闪而过,并且widget和button都析构了(在后台有打印信息)。

为了能清楚显示,创建两个全局变量

    myWidget mywidget;myButton mybutton;

然后添加代码:

//栈上创建局部对象的对象树问题mywidget.resize(300,200);mybutton.setParent(&mywidget);mywidget.show();

关闭mywidget窗口后没有反应,是因为该变量是全局变量,需要程序结束才能销毁。关闭主窗体,则析构这两个变量。

但是如果将两个变量的声明修改顺序:

    myButton mybutton;myWidget mywidget;

则会报错。这是因为C++的局部变量和全局变量释放顺序为:局部变量优先于全局变量,同级别下,先声明的后释放。

所以mywidget先释放,并将children下的mybutton释放;然后再释放mybutton时会报无法访问内存地址的错误。

总结:

在栈上创建对象,可以指定父对象也可以不指定,但是指定的父对象必须比该对象本身早;

在堆上创建对象,对于局部变量,则应指定其父对象,对于全局变量,应指定其父对象或在析构函数中进行delete回收,否则无法对该对象回收,造成内存泄漏。

QQt对象树系统及内存自动回收机制相关推荐

  1. QT对象树、信号和槽机制

    文章目录 一 .对象树是什么? 二.信号和槽的基本概念 2.1 信号 2.2 槽 2.3 松散耦合 2.4 特点 三.示例 总结 一 .对象树是什么? 对象树是由父类和若干子类对象组成,而子类也可以由 ...

  2. iOS 内存管理机制与原理

    内存分区 内存一般分为五大区:栈区.堆区.常量区.全局区.代码区.如图 1.栈区 是由编译器自动分配并释放的,主要用来存储局部变量.函数的参数等,是一块连续的内存区域,遵循先进后出(FILO)原则.一 ...

  3. Android 内存管理机制

    本文主要包括三大部分内容: 内存管理基础:从整个计算机领域简述主要的内存管理技术. Linux的内存管理机制:Android毕竟是基于Linux内核实现的操作系统,因此有必要了解一下Linux的内存管 ...

  4. python中内存管理机制一共分为多少层_python 内存管理机制

    内存管理机制 ​python中万物皆对象,python的存储问题是对象的存储问题,并且对于每个对象,python会分配一块内存空间去存储它 ​Python的内存管理机制:引入计数.垃圾回收.内存池机制 ...

  5. 【Qt教程】1.5 - Qt5内存回收机制-对象树、窗口坐标系

    1.Qt内存回收机制 - 对象树 (做了解,懂就可以,示例看视频) 当创建的对象在堆区时,如果指定的父亲是 QObject派生下来的类 或者 QObject子类派生下来的类,可以不用管理释放的操作,对 ...

  6. java 对象压缩_理解Java对象:要从内存布局及底层机制说起,话说....

    前言 大家好,又见面了,今天是JVM专题的第二篇文章,在上一篇文章中我们说了Java的类和对象在JVM中的存储方式,并使用HSDB进行佐证,没有看过上一篇文章的小伙伴可以点这里:< 这篇文章主要 ...

  7. JVM对象创建与内存分配机制学习总结

    对象的创建过程 1.类加载检查 虚拟机遇到一条new指令时(new关键词.对象克隆.对象序列化等),首先会检查这个类是否已被加载.解析和初始化过.如果没有,要先执行相应的类加载过程. 2.分配内存 内 ...

  8. 6-Qt6对象树及内存管理

    把类的对象组织成树形结构,这种树形结构也称为对象树,Qt 使用对象树来管理 QObject 及其子类的对象. 重要:当父对象析构的时候,这个列表中的所有对象也会被自动逐级析构. 如下图树形,当Pare ...

  9. linux系统内存dump机制介绍(一)--kdump

    本文来自 网易云社区 . kdump的原理介绍 按照linux系统的设计哲学,内核只提供dump内存的机制,用户想要dump什么样的内存,dump多少内存是属于策略问题,由用户来决定. 在真实的使用场 ...

最新文章

  1. 解决python3 UnicodeDecodeError: 'gbk' codec can't decode byte
  2. C#中Encoding.Unicode与Encoding.UTF8的区别
  3. I/O复用函数的使用——poll
  4. Serverless.com CEO首次访华!探讨无服务器技术落地
  5. System.gc()调用 - 适用的场景
  6. android SpannableString使用详解
  7. phpize的作用(资料整理)
  8. 安装vs2008出现MSI returned error code 1603的错误的解决
  9. cocosbuilder入门
  10. ftp主动模式与被动模式
  11. hackinglab-脚本关10——基情燃烧的岁月
  12. MiniMap(小地图)插件
  13. QQ机器人 微信机器人最新可用框架汇总
  14. 论文的主要观点怎么写?
  15. codeforces 618 C. Constellation(三角形,三点共线)
  16. Prompt-Tuning——深度解读一种新的微调范式
  17. 计算机网络相关论文目录怎么弄,如何给你的标书、论文编页码和目录-论文页码设置...
  18. php怎么判断qq内置浏览器,如何判断微信内置浏览器(JS PHP)
  19. springboot微信公众号发送模板消息
  20. 友盟多渠道打包+混淆+腾讯云直播的推/拉流

热门文章

  1. 一亩田php,【笔尖】一亩田(诗歌)
  2. zoj 2588 buring bridges (无向图求桥,含重边)
  3. 牛客网 编程初学者入门训练 BC37 网购 Java
  4. rubymine license
  5. 电脑扬声器插入耳机后才能用怎么回事?
  6. 华为交换机Console
  7. openwrt 修改路由ttl值
  8. Dogs vs. Cats数据集
  9. C语言实现库函数中的strstr查找字符串中的子串的功能
  10. 20110611日融合计费R3割接上线