深入理解Qt的隐式共享机制

在Qt中,一个关键的性能优化特性是其数据结构的隐式共享机制,这在Qt的文档和API中常被称为“隐式共享”或“写时复制(Copy-On-Write, COW)”。本文将详细介绍这一机制,并通过QString类的实现代码和相应的反汇编代码来阐释其工作原理。

隐式共享的定义和优点

隐式共享是一种内存管理策略,它允许多个对象共享相同的数据副本,直到某个对象需要修改这些数据时才进行实际的数据复制。这种策略的优点包括:

  • 减少内存使用:多个对象可以共享同一个数据副本,而不是每个对象都持有一个数据副本。
  • 提高性能:减少数据复制的需要,从而降低程序的总体资源消耗。
  • 代码简化:开发者不需要关心数据如何被共享或复制,所有这些都由Qt框架自动管理。

隐式共享在Qt中的实现

在Qt中,很多基本数据类型,如QString、QList、QByteArray等,都实现了隐式共享。以下是QString类中赋值操作符的源码,可以直观了解隐式共享机制的实现:

QString &QString::operator=(const QString &other) noexcept
{
    other.d->ref.ref();
    if (!d->ref.deref())
        Data::deallocate(d);
    d = other.d;
    return *this;
}

代码解释

  1. other.d->ref.ref();

增加源对象(other)数据的引用计数。
因为在赋值后,目标对象和源对象都引用了相同的内存数据,所以对应的引用计数需要增加。

  1. if (!d->ref.deref()) Data::deallocate(d);

通过deref()函数递减目标对象(d)的引用计数,并返回递减后是否仍有引用(如果还有其他引用,则返回true;如果此次操作后引用计数变为0,则返回false)。如果返回false,说明没有其他QString对象再引用这块数据了,那就需要调用Data::deallocate()来释放这块内存。
因为目标对象在赋值后将指向新的内存数据,那么原来的数据引用计数需要递减,如果递减后变成0,那就说明没有对象在使用这块内存,需要释放,否则会导致内存泄漏。

反汇编代码示例说明

源码

QString sa = "hello";
QString sb = sa;
sb[0] = 'H';

反汇编

    QString sa = "hello";
00007FF65A72461B  lea         rdx,[__xt_z+214h (07FF65A729CB4h)]  
00007FF65A724622  lea         rcx,[sa]  
00007FF65A724627  call        qword ptr [__imp_QString::QString (07FF65A72F170h)]  
00007FF65A72462D  nop  
    QString sb = sa;
00007FF65A72462E  lea         rdx,[sa]  
00007FF65A724633  lea         rcx,[sb]  
00007FF65A724638  call        qword ptr [__imp_QString::QString (07FF65A72F188h)]  
00007FF65A72463E  nop  
    sb[0] = 'H';
00007FF65A72463F  xor         r8d,r8d  
00007FF65A724642  lea         rdx,[rsp+88h]  
00007FF65A72464A  lea         rcx,[sb]  
00007FF65A72464F  call        qword ptr [__imp_QString::operator[] (07FF65A72F178h)]  
00007FF65A724655  mov         dl,48h  
00007FF65A724657  mov         rcx,rax  
00007FF65A72465A  call        qword ptr [__imp_QCharRef::operator= (07FF65A72F168h)]

详细说明

  1. 在执行QString sb = sa;代码是,反汇编代码可以看出,先将sasb两个对象的地址加载到两个寄存器中,然后调用QString的拷贝构造函数。
  2. 在执行sb[0] = 'H';时,反汇编代码的最后一行代码,调用了QCharRef::operator=()赋值操作符,那么我们需要知道该赋值操作符具体做了什么:
inline QCharRef &operator=(QChar c)
{
    using namespace QtPrivate::DeprecatedRefClassBehavior;
    if (Q_UNLIKELY(i >= s.d->size)) {
#ifdef QT_DEBUG
        warn(WarningType::OutOfRange, EmittingClass::QCharRef);
#endif
        s.resize(i + 1, QLatin1Char(' '));
    } else {
#ifdef QT_DEBUG
        if (Q_UNLIKELY(!s.isDetached()))
            warn(WarningType::DelayedDetach, EmittingClass::QCharRef);
#endif
        s.detach();
    }
    s.d->data()[i] = c.unicode();
    return *this;
}

我们看到上面源码中if (Q_UNLIKELY(i >= s.d->size))判断下标有没有超出字符串大小,如果超出了,则需要调用resize重新开辟一块内存,如果没有超出,则调用detach()来分离数据,在分离的时候会判断当前引用计数,如果计数大于1,则会创建新的副本数据进行修改。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UE5 动画蓝图

文章目录 一、State Machines二、Blend Spaces三、Aim Offset四、Montage 初步介绍 Unreal Engine 5 Tutorial - Animation Blueprint Part 1: State Machines (youtube.com) Unreal Engine 5 Tutorial - Animation Blueprint Part 2: Blend Spaces (youtube.com) Unreal Engi…

读人工智能全传01图灵的电子大脑

1. 人工智能 1.1. 人类对人工智能的梦想,可以追溯到很久很久以前 1.1.1. 从古希腊开始,铁匠之神赫菲斯托斯(Hephaestus)拥有赋予金属物品生命的能力 1.1.2. 从16世纪的布拉格开始,传说中伟大的拉比在那里用黏土制作了一个傀儡魔像&#xf…

使用patch-package自动修改node_modules中的内容/打补丁

背景 在使用VuePress搭建个人博客的过程中,我需要使用到一个用来复制代码块的插件uepress-plugin-nuggets-style-copy。 问题:插件可以正常安装,但是启动会报错。通过查看错误信息,定位是插件中的copy.vue文件出现错误&#xff0c…

【实战场景】记一次UAT jvm故障排查经历

【实战场景】记一次UAT jvm故障排查经历 开篇词:干货篇:1.查看系统资源使用情况2.将十进制进程号转成十六进制3.使用jstack工具监视进程的垃圾回收情况4.输出指定线程的堆内存信息5.观察日志6.本地环境复现 总结篇:我是杰叔叔,一名…

仿论坛项目--初识Spring Boot

1. 技术准备 技术架构 • Spring Boot • Spring、Spring MVC、MyBatis • Redis、Kafka、Elasticsearch • Spring Security、Spring Actuator 开发环境 • 构建工具:Apache Maven • 集成开发工具:IntelliJ IDEA • 数据库:MySQL、Redi…

Docker拉取失败,利用 Git将 Docker镜像重新打 Tag 推送到阿里云等其他公有云镜像仓库里

目录 一、开通阿里云容器镜像服务 二、Git配置 三、去DockerHub找镜像 四、编写images.txt文件 ​五、演示 六、其他注意事项 最近一段时间 Docker 镜像一直是 Pull 不下来的状态,想直连 DockerHub 是几乎不可能的。更糟糕的是,很多原本可靠的国内…

Vue+ElementUi实现录音播放上传及处理getUserMedia报错问题

1.Vue安装插件 npm install --registryhttps://registry.npmmirror.com 2.Vue页面使用 <template><div class"app-container"><!-- header --><el-header class"procedureHeader" style"height: 20px;"><el-divid…

密码学及其应用 —— 密码学的经典问题

1. 古典密码学问题 1.1 问题1&#xff1a;破解凯撒密码 1.1.1 问题 凯撒密码是最简单的单字母替换加密方案。这是一种通过将字母表中的字母固定向右移动几位来实现的加密方法。解密下面的文本&#xff0c;该文本通过对一个去除了空格的法语文本应用凯撒密码获得&#xff1a; …

layui-按钮

1.用法 使用 用button标签 type"button" class"layui-button" 效果&#xff1a; 2.主题设置 前面都要加上layui-bin 3.尺寸设置 可以叠加使用&#xff01; 4.圆角设置 加一个layui-bin-radius 5.按钮图标设置 里面加一个i标签 加class"layui-…

借教室(题解)

P1083 [NOIP2012 提高组] 借教室 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路&#xff1a;二分前缀和 我们将和质检员那题差不多&#xff0c;只需要将候选人二分即可 #include<bits/stdc.h> using namespace std; #define int long long int n,m; int r[100000…

【操作与配置】VSCode配置Python

Python环境配置 可以参见&#xff1a;【操作与配置】Python&#xff1a;CondaPycharm_pycharmconda-CSDN博客 官网下载Python&#xff1a;http://www.python.org/download/官网下载Conda&#xff1a;Miniconda — Anaconda documentation VSCode插件安装 插件安装后需重启V…

disql使用

进入bin目录&#xff1a;cd /opt/dmdbms/bin 启动disql&#xff1a;./disql&#xff0c;然后输入用户名、密码 sh文件直接使用disql&#xff1a; 临时添加路径到PATH环境变量&#xff1a;在当前会话中临时使用disql命令而无需每次都写完整路径&#xff0c;可以在执行脚本之前…

Eclipse + GDB + J-Link 的单片机程序调试实践

Eclipse GDB J-Link 的调试实践 本文介绍如何创建Eclipse的调试配置&#xff0c;如何控制调试过程&#xff0c;如何查看修改各种变量。 对 Eclipse 的要求 所用 Eclipse 应当安装了 Eclipse Embedded CDT 插件。从 https://www.eclipse.org/downloads/packages/ 下载 Ecli…

20240628模拟赛总结

cf好了 让我们开始 T1 Two Regular Polygons 判断能不能构造出题中要求的正多边形 关键是n%m0 Two Regular Polygons #include<bits/stdc.h> using namespace std; int t; int n,m; int main() {cin>>t;for(int i1;i<t;i){cin>>n>>m;if(n%m0)co…

MySQL 代理层:ProxySQL

文章目录 说明安装部署1.1 yum 安装1.2 启停管理1.3 查询版本1.4 Admin 管理接口 入门体验功能介绍3.1 多层次配置系统 读写分离将实例接入到代理服务定义主机组之间的复制关系配置路由规则事务读的配置延迟阈值和请求转发 ProxySQL 核心表mysql_usersmysql_serversmysql_repli…

【C++】相机标定源码笔记- 标定工具库测试

标定工具库测试 一、计算相机内参&#xff1a;对两个相机进行内参标定&#xff0c;并将标定结果保存到指定的文件中 采集图像&#xff1a;相机1-16张 相机2-17张 定义保存相机1/2内参的文件(.yml)路径。 定义相机1/2采集的图片文件夹路径。定义相机1/2存储文件名的向量获取文件…

作为图形渲染API,OpenGL和Direct3D的全方位对比。

当你在网页看到很多美轮美奂的图形效果&#xff0c;3D交互效果&#xff0c;你知道是如何实现的吗&#xff1f;当然是借助图形渲染API了&#xff0c;说起这个不就不得说两大阵营&#xff0c;OpenGL和Direct3D&#xff0c;贝格前端工场在本文对二者做个详细对比。 一、什么是图形…

26.5 Django模板层

1. 模版介绍 在Django中, 模板(Templates)主要用于动态地生成HTML页面. 当需要基于某些数据(如用户信息, 数据库查询结果等)来动态地渲染HTML页面时, 就会使用到模板.以下是模板在Django中使用的几个关键场景: * 1. 动态内容生成: 当需要根据数据库中的数据或其他动态数据来生…

推动能源绿色低碳发展,风机巡检进入国产超高清+AI时代

全球绿色低碳能源数字转型发展正在进入一个重要窗口期。风电作为一种清洁能源&#xff0c;在碳中和过程中扮演重要角色&#xff0c;但风电场运维却是一件十足的“苦差事”。 传统的风机叶片人工巡检方式主要依靠巡检人员利用高倍望远镜检查、高空绕行下降目测检查(蜘蛛人)、叶…

校园水质信息化监管系统——水质监管物联网系统

随着物联网技术的发展越来越成熟&#xff0c;它不断地与人们的日常生活和工作深入融合&#xff0c;推动着社会的进步。其中物联网系统集成在高校实践课程中可以应用到许多项目&#xff0c;如环境气象检测、花卉种植信息化监管、水质信息化监管、校园设施物联网信息化改造、停车…