【QT】对象树

news2024/11/14 17:23:50

一、QT对象树的概念

先来看一下 QObject 的构造函数:

img

通过帮助文档我们可以看到,QObject 的构造函数中会传入一个 Parent 父对象指针,children() 函数返回 QObjectList。即每一个 QObject 对象有且仅有一个父对象,但可以有很多个子对象。按照这种形式排列就会形成一个对象树的结构,最上层是父对象,下面是子对象,在再下面是孙子对象,以此类推。

那么Qt为什么要这么设计呢?或者说这样设计的好处是什么呢?很简单,就是为了方便内存管理。我们在创建 QObject 对象时,提供一个父对象,那么我们创建的这个 QObject 对象会自动添加到其父对象的 children() 列表。当父对象析构的时候,这个子对象列表中的所有对象都会被析构,当析构子对象的时候,会自动从父对象的子对象列表中删除

这种机制在 GUI 程序开发过程中是相当实用的。有一个很明显的现象就是我们会在窗口中new很多控件,但是却没有delete,因为在父控件销毁时这些子控件以及布局管理器对象会一并销毁。

值得注意的是,如果在构造时设置父对象为 NULL,那么当前实例不会有父对象存在,Qt 也不会自动析构该实例,除非实例超出作用域导致析构函数被调用,或者用户在恰当时机使用 delete 操作符或者使用 deleteLater 方法。


在这里插入图片描述


QWidget

QWidget 也是 QObject 的子类,所以在 parent 机制上是没有区别的。然而实际使用时,对于 QWidget 和其派生类来说,在内存管理上要稍微复杂一些。因为 QWidget 需要和 QEventLoop 高度配合才能完成工作,我们举个例子来说明一下。

例如 QWidget 的关闭流程,首先用户点击关闭按钮触发 close() 槽,然后Qt向 widget 发送 QCloseEvent,默认的 QCloseEvent 会将 widget 隐藏起来,也就是hide()。我们可以看到,widget 的关闭实际是将其隐藏,而没有释放内存,虽然我们有时会重写 closeEvent 但也不会手动释放 widget。

所以需要设置 Qt::WA_DeleteOnClose 属性,那么会在 close 之后接着调用 widget 的析构函数,或者手动 delete。




二、对象树模型存在的小问题

示例代码1:

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QPushButton btn("button");
    QWidget w;
    btn.setParent(&w);
    w.show();

    return a.exec();
}

运行结果: 关闭 widget 后程序崩溃,没有正常结束

程序异常结束。

原因:

btn.setParent(&w);

btn对象,通过setParent()函数,将自己挂到对象树上。当程序结束时,根据栈区的特性,应该是先析构父对象–w,再析构btn可是由于Qt中的对象树自动析构原理,我们析构父对象会自动析构子对象。也就是说,在析构父对象–w时,会自动调用子对象btn的析构函数。然后,栈区继续销毁,按照顺序还要再一次析构btn。但是这时候已经是第二次调用 子对象的析构函数了,C++中不允许调用两次析构函数,因此,程序会崩溃。


在这里插入图片描述




三、正确写法–尽量在堆上创建子对象

示例代码2:

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QPushButton *btn = new QPushButton ("button");
    QWidget *w = new QWidget;
    btn->setParent(w);
    w->show();

    return a.exec();
}

w和btn被分配在堆上。w析构时,不会影响在栈区中的对象,程序正常运行。


由此我们可以看到,Qt 的对象树机制虽然在内存管理上很方便,但是也会带来一些麻烦,为了避免这些麻烦我们可以这么做:

  1. 先创建父对象再创建子类对象,并且在创建子对象时就指定父对象;
  2. 尽量在堆上创建子对象;



参考链接:

Qt 对象树

Qt中的内存泄漏

[Qt源码阅读(三) 对象树管理]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1135099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是间谍软件恶意软件?

攻击者利用此类软件从受感染的设备中收集和传输信息。这可能包括个人、银行和用户凭证&#xff08;社交网络、流媒体服务、邮件等的登录名和密码&#xff09;&#xff0c;以及设备内存中的文件。 在间谍软件的帮助下&#xff0c;欺诈者还可以窃取公司凭证&#xff0c;不仅伤害…

Vue 3中toRaw和markRaw的使用

文章目录 Vue 3的响应性系统使用toRaw使用markRaw使用场景1. 与第三方库交互2. 提高性能3. 避免无限循环 总结 &#x1f389;欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&#…

Linux基础:2:shell外壳+文件权限

shell外壳文件权限 一.shell原理&#xff1a;1.对比&#xff1a;windo GUI 和 shell1.windo GUI2. shell 2.为什么&#xff1f;是什么&#xff1f;怎么办&#xff1f;1.为什么有shell2.是什么&#xff1f;3.怎么办&#xff1f;4.补充&#xff1a; 二.linux权限管理&#xff1a;…

Linux redis 安装

1、解压 tar -zxvf redis-5.0.10.tar.gz 2、cd /data/redis-5.0.10 文件夹 3、make 等待make命令执行完成即可。 make命令报错&#xff1a;cc 未找到命令&#xff0c;系统中缺少gcc&#xff0c;执行命令安装 gcc&#xff1a; yum -y install gcc automake autocon…

蓝桥杯每日一题2023.10.25

乘积尾零 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 由于需要相乘的数很多&#xff0c;所以我们不能直接进行暴力模拟&#xff0c;我们知道10 2 * 5&#xff0c; 所以我们只需要找出这个数2和5的个数&#xff0c;其中2和5个数小的那个则为末尾0出现的个数 #include<bi…

探讨安科瑞低压备自投装置AM5-DB的应用-安科瑞 蒋静

1 概况 本项目主要为发电机组供电切换提供备自投保护功能&#xff0c;该发电机组的供电主要由两进线一母联的系统组成&#xff0c;其中&#xff0c;每路进线分别由一路市电和一路柴油发电机切换提供&#xff0c;故此&#xff0c;我司用了三台低压备自投装置AM5-DB来实现备自投…

软信天成:助力某制造企业建设产品主数据管理平台案例分享

某国有大型传统制造企业是一家跨领域、跨行业经营的国际化公司&#xff0c;在全球范围内拥有动力系统、工程机械、汽车制造等多个业务板块、分子公司遍及世界、产品远销110多个国家和地区&#xff0c;产品品类繁多&#xff0c;分支架构错综复杂。 近年来&#xff0c;数字化的深…

欢迎浪潮KaiwuDB成为开源社白金合作伙伴

众多开源爱好者翘首期盼的开源盛会&#xff1a;第八届中国开源年会&#xff08;COSCon23&#xff09;将于 10月28-29日在四川成都市高新区菁蓉汇举办。本次大会的主题是&#xff1a;“开源&#xff1a;川流不息、山海相映”&#xff01;各位新老朋友们&#xff0c;欢迎到成都&a…

Find My护照|苹果Find My技术与护照结合,智能防丢,全球定位

护照是一个国家的公民出入本国国境和到国外旅行或居留时&#xff0c; 由本国发给的一种证明该公民国籍和身份的合法证件。 护照&#xff08;PASSPORT&#xff09;一词在英文中是口岸通行证的意思。也就是说&#xff0c; 护照是公民旅行通过各国国际口岸的一种通行证明。 护照…

非对称加密---椭圆曲线---单向散列函数

4. 非对称加密 "非对称加密也叫公钥密码: 使用公钥加密, 使用私钥解密"在对称密码中&#xff0c;由于加密和解密的密钥是相同的&#xff0c;因此必须向接收者配送密钥。用于解密的密钥必须被配送给接收者&#xff0c;这一问题称为密钥配送问题。如果使用非对称加密也…

C语言求数组最小值,并返回下标

#include<stdio.h> #include<string.h> void main() {int a[] {12,5,21,33,55,77,11,2};int zf sizeof(a) / sizeof(a[0]);for (int i 0; i < zf; i){if (a[i] < a[0]){a[0] a[i];}}printf("最小为%d&#xff0c;下标是0",a[0]); } 如果是求最…

markdown操作

一、语法设置 1.1 标题 1.1.1 使用’#表示标题 #必须在行首&#xff0c;#越多&#xff0c;标题的级别越低1.2 代码块 1.2.1 行内代码块 使用反单引号&#xff08;一般在键盘的左上位置&#xff0c;和~是一个键&#xff09;来表示&#xff0c;如 hello&#xff1a;hello 1.2…

Windows一些基础设置

开机自启动 方法一&#xff1a; 1、首先按下键盘上的“Wini”打开设置窗口&#xff0c;在左边找到并进入“应用”&#xff1b; 2、接着在右侧点击并进入“启动”设置。 3、在启动应用下&#xff0c;将不需要开机自启动的软件给关掉。 方法二&#xff1a; 1、按CtrlShiftEs…

使用HXT和Haskell编写的程序

以下是一个使用HXT和Haskell编写的下载器程序&#xff0c;用于下载内容。此程序使用了proxy的代码。 -- 导入必要的库 import Network.HTTP.Conduit import Network.HTTP.Types.Status import Network.HTTP.Types.Header import Data.Conduit import Data.Conduit.Network imp…

Python 算法高级篇:布谷鸟哈希算法与分布式哈希表

Python 算法高级篇&#xff1a;布谷鸟哈希算法与分布式哈希表 引言 1. 什么是哈希算法&#xff1f;1.1 哈希算法的用途 2. 布谷鸟哈希算法2.1 布谷鸟哈希表的特点2.2 布谷鸟哈希算法的伪代码2.3 Python 中的布谷鸟哈希算法实现 3. 分布式哈希表3.1 分布式哈希表的特点3.2 一致性…

为啥外行都觉得程序员的代码不值钱?

点击下方“JavaEdge”&#xff0c;选择“设为星标” 第一时间关注技术干货&#xff01; 免责声明~ 任何文章不要过度深思&#xff01; 万事万物都经不起审视&#xff0c;因为世上没有同样的成长环境&#xff0c;也没有同样的认知水平&#xff0c;更「没有适用于所有人的解决方案…

根据输入类型来选择函数不同的实现方法functools.singledispatch

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 根据输入类型来选择函数不同的实现方法 functools.singledispatch 输入6后&#xff0c;下列输出正确的是&#xff1f; from functools import singledispatch singledispatch def calcu…

讯飞输入法13.0发布,推出行业首款生成式AI输入法

&#x1f989; AI新闻 &#x1f680; 讯飞输入法13.0发布&#xff0c;推出行业首款生成式AI输入法 摘要&#xff1a;科大讯飞在2023年全球开发者节上发布了全新讯飞输入法13.0版本&#xff0c;其中最大的亮点是推出了行业首款生成式AI输入法。这次升级将生成式AI能力融入输入…

Wt库的C++下载器程序

以下是一个使用Wt库的C下载器程序&#xff0c;用于下载音频文件。此程序使用了的代码。 #include <Wt/Wt.h> #include <Wt/Http/DiskCache.h> #include <Wt/Http/HttpClient.h> ​ // 定义一个函数来获取服务器 static std::string get_proxy() {// 使用Wt:…

【c++】——类和对象(上)——万字详细解疑

目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 3.1类的两种定义方式&#xff1a; 3.2 成员变量命名规则建议 4.类的访问限定符及封装 4.1 访问限定符 4.2.封装 5.类的作用域 6.类的实例化 7.类对象模型 7.1 如何计算类对象的大小 7.2 类对象的存储方式…