Qt隐式共享detach函数的理解

别再写低效循环了:深入理解Qt隐式共享与C++17的std::as_const 本文深入探讨了Qt隐式共享机制与C++17的std::as_const,解析了范围循环中的性能陷阱,并对比了Qt的qAsConst与std::as_const的差异。通过实际案例和最佳实践,帮助开发者编写更高效的现代C++代码,避免不必要的分离操作,提升性能。 阅读详情

隐式数据共享
Qt隐式共享的详细描述,请在Qt Assistant 索引查阅,如下:

以下是本人的理解:

Qt 中的许多 C++ 类使用隐式数据共享来最大化资源使用并最小化复制。当作为参数传递时,因为只传递指向数据的指针,并且只有在函数写入时才复制数据(即写时复制),故隐式共享类既安全又高效,

共享类由指向共享数据块的指针组成,该指针包含引用计数和数据。

创建共享对象时,它将引用计数设置为 1。每当新对象引用共享数据时,引用计数就会增加,当对象取消引用共享数据时,引用计数会减少。当引用计数变为零时,共享数据将被删除,类似C++中的共享智能指针。

在处理共享对象时,有两种复制对象的方法:深拷贝和浅拷贝。

  • 深拷贝意味着复制一个对象。
  • 浅拷贝是引用拷贝,即只拷贝指向共享数据块的指针,而不是拷贝指针指向的对象。

就内存和 CPU 而言,进行深度复制可能会很昂贵。进行浅拷贝非常快,因为它只涉及设置指针和增加引用计数。

隐式共享对象的对象分配(使用 operator=())是使用浅拷贝实现的。

共享的好处是程序不需要不必要地复制数据,从而减少内存使用和数据复制。对象可以很容易地被赋值,作为函数参数发送,并从函数中返回。


如下A为对象,且当前只有一个A类型的指针指向该对象,暂且称为pA1,类型为A*,

此时A*的类内部的引用计数refCount 为1。当有N个A类型的指针指向A对象时,即如下图所示:

此时A*的类内部的引用计数refCount 为n,可以看到这些指针都是共享A,并没有单独复制一份A对象,即没有深度复制A对象,也就是所谓的浅拷贝。当需要对某个指针指向的A对象的属性进行更新、修改时,其它指针的调用方此时发现其所指向的A也跟着改变了(因为它们都是指向同一个A对象),这在很多情况下,不是调用方所希望的结果。比如:A是QPen对象,如下代码:


  void QPen::setStyle(Qt::PenStyle style)
  {
      
      d->style = style;   // set the style member
  }

  ......................// 其它代码
QPen* pPen1 = new QPen; // QPen的引用计数为1
QPen* pPen2 = pPen1;    // QPen的引用计数为2

// 刚new出来的QPen对象被pPen2更改了,此时pPen1的调用方也跟着变化了,此时pPen1的调用方
// 功能就会不正确,就会感觉诡异,心里暗想:我明明啥都没改,怎么画笔就变了呢?
pPen2->setStyle(Qt::CustomDashLine); 

刚new出来的QPen对象被pPen2更改了,此时pPen1的调用方也跟着变化了,此时pPen1的调用方
功能就会不正确,就会感觉诡异,心里暗想:我明明啥都没改,怎么画笔就变了呢?detach正是为了解决这个问题而提出!!!对于上面情况,detach()函数内部实现机制是:

  1. 先深度拷贝一个A对象,拿上面的代码来说就是深度复制出一个QPen。
  2. 更新操作只在步骤1深度拷贝出的对象上进行。

即:

 void QPen::setStyle(Qt::PenStyle style)
  {
      detach();           // detach from common data

      // 这里的d是detach()函数深度拷贝出来的新对象,而不再是原来的那个。
      d->style = style;   // set the style member
  }

  void QPen::detach()
  {
      if (d->ref != 1) {
          ...   // 发现引用计数大于1,证明其它地方也在调用该对象,那么就深度拷贝一个该对象
      }
  }

即按下图进行:

pA2即为要更新的对象。先将pA2指向A对象断开,再深度复制一个A,让 pA2指向新复制出来的这个A对象。此时对新复制出来的A无论怎么修改都不会影响到原来的A对象,即非pA2的指针的调用方不会受影响。

对于QSharedDataPointer类型(QSharedDataPointer用法请参考《QPointer、QScopedPointer、QSharedDataPointer等指针用法总结》)的clone函数,当引用计数大于 1 时,clone函数由 detach() 调用以创建新副本。此函数使用运算符 new 并调用类型 T 的拷贝构造函数。QSharedDataPointer类型的的T *data() / T *get(),返回指向共享数据对象的指针。这两个函数也调用 detach()。QSharedDataPointer类型设计clone、data() 、 get()函数的目的就是为了对指向的对象进行更改,所以必须是深度复制才行。QSharedDataPointer类型的const T *constData() 、const T *data()  / const T *get() 设计目的从const就可以看出仅仅是读取指向的对象,而不是进行写入更改操作,故带const的这三个函数不会调用detach,因为浅拷贝就可以完成,用浅拷贝进行读取共享对象不会导致被读取的共享对象更改,所以没必要进行资源高、效率低的深拷贝。

总结:detach发生两个必要条件是:

  • 隐式共享的对象个数超过1个,即引用计数大于1.
  • 即将要detach的对象被更改。
QByteArray内存模型深度解析:隐式共享、零拷贝与工业级避坑指南 QByteArray是Qt中用于高效二进制数据处理的核心类,其本质并非简单字节数组,而是基于隐式共享(Copy-on-Write)、堆内元数据管理与预分配策略构建的内存契约载体。它通过引用计数实现多对象安全共享,避免深拷贝开销;借助capacity()非线性增长(2的幂次扩容)减少realloc频率;并严格区分constData()(只读、零开销)与data()(写入触发detach)以保障线程安全与性能。在Modbus协议解析、音视频流处理、网络收发等高频数据流转场景中,正确理解其内存行为可显著降低CPU 阅读详情

相关推荐

QT排序函数深度解析:qSort、qStableSort与qPartialSort实战指南

排序是数据处理中的基础操作,其核心原理是通过比较和交换元素位置,使数据集合按照特定规则有序排列。在C++开发中,标准库提供了std::sort等通用算法,但在QT框架下,针对其特有的容器和内存管理机制,使用专门的排序函数能带来更好的性能和安全性。QT的qSort、qStableSort和qPartialSort函数不仅封装了高效的排序算法,还深度集成了隐式共享(Copy-On-Write)机制,这对于处理包含QObject派生类或复杂数据结构的容器至关重要。理解这些函数的差异,能帮助开发者在GUI数据处理、

weixin_30367169的博客 483

从‘隐式共享’到‘qAsConst’:深入理解Qt容器遍历的性能守护神

本文深入探讨了Qt容器遍历的性能优化,从隐式共享机制到qAsConst的应用。通过对比foreach与范围for循环的性能差异,揭示了qAsConst如何成为Qt容器遍历的'性能守护神',有效避免隐式共享带来的意外detach操作,提升大型数据集处理效率。

weixin_30552811的博客 292

Win10家庭版没有Hyper-V?教你一招轻松解决(含避坑指南)

本文详细介绍了在Win10家庭版中启用Hyper-V的完整实战指南。通过DISM工具链绕过系统版本限制,无需重装系统即可完整启用所有Hyper-V组件,适用于开发者测试多系统环境和安全隔离应用场景。文章包含详细操作步骤、常见问题解决方案及性能优化配置,帮助用户轻松解决Win10家庭版没有Hyper-V的问题。

weixin_29239661的博客 495

qt 进程 线程 detach --- 无限重启程序

#include "SoftWareUpdate.h" #include <QApplication> #include <QDesktopWidget> // when need updater update void updaterUpdate() { sleep(5); // QProcess p; //p.startDetached("killall updater"); // p.waitForStarted(2); //sleep(...

weixin_44075027的博客 1403

【Ultrascale+ MPSOC学习记录】10、PS端以太网使用lwip

还是参考ALINX教程,学习PS端以太网如何使用,从教程中可以看到,开发板使用了一个xilinx默认不支持的phy JL2121,所以需要修改lwip的库源码。

ryo891012的博客 880

群晖NAS实战:从零部署Container Manager与Docker代理配置全攻略

本文详细介绍了在群晖NAS上从零部署Container Manager与配置Docker代理的完整流程。针对国内用户常见的镜像拉取问题,提供了图形化界面与SSH命令行两种解决方案,并分享了MySQL容器部署的实战案例。文章特别强调了网络配置技巧和日常维护方法,帮助用户高效管理NAS上的容器应用。

weixin_29254673的博客 1302

Qt中的隐式数据共享、QSharedDataPointer、QExplicitlySharedDataPointer

一、隐式数据共享 Qt 中的许多 C++ 类使用隐式数据共享来最大化资源使用并最小化复制。当作为参数传递时,隐式共享类既安全又高效,因为只传递指向数据的指针,并且只有在函数写入时才复制数据,即写时复制。 共享类由指向共享数据块的指针组成,该指针包含引用计数和数据。 创建共享对象时,它将引用计数设置为 1。每当新对象引用共享数据时,引用计数就会增加,当对象取消引用共享数据时,引用计数会减少。当引用计数变为零时,共享数据将被删除。 在处理共享对象时,有两种复制对象的方法:深拷贝和浅拷贝。 深拷贝意味着

友善啊,朋友的博客 4999

基于Quartus II软件的FPGA综合实验——多功能数字钟

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、设计要求二、设计原理三、设计过程1.数码管扫描模块2.计时模块3.闹钟模块4.闹钟响铃模块5.数码管显示模块6.整点报时功能7.模式选择模块(计时器主控电路)四、实验中遇到的困难及解决方案五、总结 前言 FPGA综合实验——多功能数字钟 基于Quartus II软件 一、设计要求 设计一个能进行时、分、秒计时的十二小时制或二十四小时制的数字钟,并具有定时与闹钟功能,能在设定的时间发出闹铃音,能非常方便地对小时、分钟和秒进.

qq_53022342的博客 2万+

QT之QSharedMemory 详解 进程间通信

QSharedMemory的几个重要接口: setKey(),这是标记共享内存的一个标识符, 在整个操作系统的共享内存中,用这个key去标识它,唯一标识。 create(),创建共享内存,向os申请内存空间, 如果不创建,调用attach()会失败, 在create()的时候,需要制定共享内存的大小,即字节数, 跟windows的CreateFileMapping()一样,都是需要在create...

SmallBottle-CGWLMX的博客 9733

大学生智能车竞赛初次尝试经验分享--1(硬件绘制电路,pcb部分)

本人机械电子专业大二暑假参加全国大学生智能车竞赛,之前参加过一些其他类型的比赛有一点基础,由于之前疫情影响所以做智能车比赛断代了,没有学长带也没有思路,从一开始的一头雾水逐渐摸索出我感觉比较实用的经验。 一.首先是硬件的准备工作: 1.硬件部分与要自己设计需要的电路制成pcb。推荐使用Altium Designer20,AD比较常用所以有多实用的教学视频,另外需要自己了解布线规则,包括信号线,电源线常用的宽度 一些电源电路,驱动电路建议用multisim仿真,没有问题再进行制版 单片机有关的需要仿真

weixin_52679034的博客 9833

1分钟了解LangChain是什么?

是一个基于大型语言模型(LLM)开发应用程序的框架, 它旨在简化语言模型应用的开发流程,特别是在构建对话系统和其他基于语言的AI解决方案时.目标是将复杂的语言模型技术转化为可通过简单API调用实现的功能,从而使开发者能够更加专注于创造性的解决方案设计。高效地解决了开发大语言模型应用的痛点问题。Langchain 最主要的特色是可以将 LLM 应用研发过程中的交互 Prompt、LLM 模型调用、语言模型与环境互动的自适应等方式融为一体。

gorgor在码农 2630

Qt 中的多线程(二)

可重入与线程安全在Qt文档中,术语“可重入”与“线程安全”被用来说明一个函数如何用于多线程程序。假如一个类的任何函数在此类的多个不同的实例上,可以被多个线程同时调用,那么这个类被称为是“可重入”的。假如不同的线程作用在同一个实例上仍可以正常工作,那么称之为“线程安全”的。大多数c++类天生就是可重入的,因为它们典型地仅仅引用成员数据。任何线程可以在类的一个实例上调用这样的成员函数,只要没有别的

在水一方 1468

C# + YOLO 实时摄像头检测:Emgu CV + ONNX Runtime,30FPS+ 流畅运行

很多教程只给“单张图片推理”代码,一上摄像头就卡成 PPT(<5 FPS)。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 215

在多线程程序中detach的使用引发的问题

创建临时对象为线程参数 用detach时,子线程引用主函数中的参数会引发很多问题,例如:变量还没有复制到子线程中就已经被收回。 若传递int这种简单类型参数,建议是值传递,必要用引用和指针,避免界外生枝。 如果传递类对象,避免隐式类型转换。全部在创建线程这一行构建出临时对象来,然后在函数参数里,用引用来接,否者会多构造一次对象。 建议不适用detach(),只使用join():这样就不会出现局...

chenbiyun940419的博客 2503

《从底层结构开始学习FPGA》目录与传送门

《从底层结构开始学习FPGA》目录与传送门

孤独的单刀 16万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

深入Qt容器隐式共享:从‘detach警告’理解qAsConst和C++11 for循环的正确姿势

本文深入探讨了Qt容器的隐式共享机制,解析了C++11范围for循环中常见的'detach警告'问题。通过对比分析直接遍历、const引用和qAsConst三种方式,详细介绍了如何避免不必要的性能开销,并提供了qAsConst的最佳实践和现代C++替代方案,帮助开发者编写更高效的Qt/C++代码。

weixin_30399821的博客 557

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

QT 隐式共享/写时复制详解

b = a;a[0] = 5;*i = 12;输出结果是:5 0 05 12 12是不是有些出人意料?为何会出现这种现象,原因是QVector是隐式共享(copy-on-write)的容器。以上代码中,b = a;并不会马上复制一份独立的数据。它只是让b和a共享同一块内部数据缓冲区。a和b共享同一个数据内存,i仍然指向那块内存。本质上指向内部数组,当修改a或b时,若发生写时复制(detach),迭代器可能失效。修改迭代器 i 就是用了隐式共享容器的 “裸指针式” 修改方式。

宏笋的博客 307

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Qt隐式共享机制详解:从Copy-on-Write原理到detach性能排查实战

在C++高性能应用开发中,值类型数据的复制效率直接影响程序运行速度与内存表现。Copy-on-Write(写时复制)作为一种经典优化策略,通过共享底层数据块并在写入时触发深拷贝,让看似昂贵的拷贝操作变成近乎零成本的引用计数增加。这一机制在Qt框架中体现为隐式共享(Implicit Sharing),广泛应用于QString、QList、QImage等核心类型。理解其底层原理与detach触发条件,对于规避意外的深拷贝、迭代器失效及多线程数据竞争至关重要。从只读遍历时误用begin()引发的性能杀手,到自定义

weixin_30984001的博客 164

Qt隐式共享与写时复制:原理、深拷贝触发与多线程实践

在C++开发中,值语义的拷贝开销常成为性能瓶颈,尤其当数据量庞大时,深拷贝会导致内存飙升和界面卡顿。隐式共享(Implicit Sharing)通过引用计数与写时复制(Copy-On-Write)机制,让多个对象共享同一份数据,仅在修改时分离,从而大幅降低拷贝成本。这一技术不仅优化内存与缓存,更在Qt框架的容器、字符串和图像类中广泛应用。理解其原理,能帮助开发者在多线程、迭代器遍历和自定义类型等场景中避免误用,例如非const操作意外触发深拷贝。掌握隐式共享,是提升Qt程序性能与稳定性的关键。

weixin_33851177的博客 373

Qt隐式共享与写时复制:原理、性能优化与线程安全实战

在C++开发中,值语义容器的拷贝开销常导致内存暴涨和性能下降。隐式共享(Implicit Sharing)通过引用计数和写时复制(Copy-On-Write)机制,让多个对象共享同一份数据,仅在修改时分离副本,从而在保持值语义易用性的同时大幅降低拷贝成本。该技术广泛应用于Qt的QString、QList、QImage等容器,能有效优化内存占用与程序响应速度。典型应用场景包括桌面端工具、图像处理及跨线程数据传递。本文深入剖析Qt隐式共享的底层实现、触发条件与线程安全边界,并分享实战性能优化经验。

weixin_33862993的博客 158

Qt QString深入解析:隐式共享、编码转换与性能优化

在C++与Qt开发中,字符串处理始终是绕不开的基础议题。从标准模板库的std::string到Qt框架的QString,不同字符串类的设计哲学直接影响着代码的效率与可维护性。QString通过隐式共享与写时复制机制,实现了低成本的值传递与返回,大幅降低了对象拷贝开销;其基于UTF-16的内部存储模型则为Unicode文本处理提供了统一底座,但编码转换与跨平台乱码也因此成为高频痛点。理解QString的构造、拼接、查找替换及格式化API,能显著提升文本处理效率;掌握其深拷贝触发边界、内存分配策略以及与QByt

weixin_34199335的博客 361

Qt容器(QStringList)在C++11范围循环中的隐式分离风险与qAsConst实战解析

本文深入解析了在C++11范围循环中使用Qt容器(如QStringList)时可能触发的隐式共享分离风险,揭示了其背后的性能陷阱。通过对比常量引用与qAsConst宏两种解决方案,重点推荐使用qAsConst来确保只读遍历的安全性,避免不必要的容器拷贝,从而显著提升Qt应用程序的运行效率。

weixin_29009339的博客 352

QString深度解析:从隐式共享到中文乱码,全面掌握Qt字符串机制

字符串是编程中最常见的数据形态,其编码模型和内存布局直接影响程序的性能与稳定性。在C++生态中,不同库对字符串的实现各有取舍,Qt框架中的QString选择UTF-16作为内部存储,并引入隐式共享机制,使复制成本趋近于零,而首次修改时才触发真正的数据分离。理解这种设计,有助于开发者避开中文乱码、资源占用和指针悬垂等工程陷阱。无论是界面显示、文件读写还是跨语言数据交换,QString都承担着Qt应用的核心文本载体。围绕其高频操作、格式化与正则匹配,掌握正确的性能取舍与互转边界,能让字符串处理更高效、更安全。结

weixin_34209851的博客 344

Qt隐式共享深度解析:从内存暴涨到写时复制实战

在C++开发中,值语义与拷贝开销常令人纠结。Qt通过隐式共享(Implicit Sharing)与写时复制(Copy-on-Write)机制,让QString、QVector等核心类在按值传递时仅增加引用计数,修改时才真正复制数据,从而大幅降低内存占用。其原理基于d指针与原子引用计数,适用于读多写少场景,如图像处理、配置传递。但跨线程共享或频繁修改时需主动detach,避免迭代器失效与性能下降。理解这一机制,能帮助开发者写出更高效、更安全的Qt代码。

weixin_34406086的博客 279

foreach、qAsConst用法总结

Qt及STL容器基于foreach、范围循环区别

danshiming的博客 5938

Qt之共享内存类QSharedMemory的使用及实现原理(全)(一)

跨平台支持:对不同操作系统(如 Windows、Linux 等)的底层共享内存机制进行封装,屏蔽了各系统间的差异,为开发者提供统一的接口,方便在不同平台上实现进程间共享内存通信。简化操作:提供了创建、附加、分离、删除共享内存等操作的简单方法,降低了使用共享内存的复杂度,开发者无需深入了解底层系统调用细节。数据共享:允许不同进程或线程访问同一块物理内存区域,实现高效的数据共享,避免了数据在不同进程间的多次拷贝,提高了数据传输效率。从使用的角度来讲,尤其是在线程或进程间数据通信来讲还是非常方便的;

红军不怕远征难,万水千山只等闲 2706
上一篇: Linux下查看json利器
下一篇: 保存csdn网页到本地打开时自动跳转到首页解决方法
荆楚闲人
博客等级 码龄20年 3万+粉丝 473原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值