Qt 隐式共享

embedded/2025/3/30 12:58:10/

隐性共享

Qt 中的许多 C++ 类都使用隐式数据共享,以最大限度地提高资源利用率并减少复制。隐式共享类在作为参数传递时既安全又高效,因为只传递指向数据的指针,只有在函数写入数据时才复制数据,即时复制。

概述

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

每当有新对象引用共享数据时,引用计数就会递增,而当对象取消引用共享数据时,引用计数就会递减。当引用计数变为 0 时,共享数据将被删除。

在处理共享对象时,有两种复制对象的方法。我们通常说的是拷贝和拷贝。深度拷贝意味着复制一个对象。浅层拷贝是引用拷贝,即只是指向共享数据块的指针。深度拷贝需要耗费大量内存和 CPU。浅层拷贝的速度非常快,因为它只需要设置一个指针并增加引用计数。

隐式共享对象的对象赋值(使用 operator=())就是通过浅层拷贝实现的。

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

隐式共享大多发生在幕后,程序员很少需要担心。不过,Qt 的容器迭代器的行为与 STL 的容器迭代器不同。阅读隐式共享迭代器问题。

在多线程应用程序中,隐式共享会发生,详见《线程和隐式共享类》(Threads and Implicitly Shared Classes)。

在实现您自己的隐式共享类时,请使用QSharedData 和QSharedDataPointer 类。

线程和隐式共享类

        Qt 对其许多值类(尤其是QImage 和QString )使用了一种称为隐式共享的优化方法。从 Qt 4 开始,隐式共享类可以像其他值类一样安全地跨线程复制。它们是完全可重入的(不是线程安全)。隐式共享确实是隐式的

        在很多人的印象中,隐式共享和多线程是不相容的概念,因为引用计数通常是这样进行的。然而,Qt 使用原子引用计数来确保共享数据的完整性,避免了引用计数器的潜在损坏。

        请注意,原子引用计数并不能保证线程安全。在线程间共享隐式共享类的实例时,应使用适当的锁定。这是对所有重入类的相同要求,无论是否共享。不过,原子引用计数确实能保证在隐式共享类的本地实例上工作的线程是安全的(意思是不同对象在不同线程里是安全,同一对象在多线程不是安全的)。我们建议使用信号和槽在线程间传递数据,因为这样做无需任何显式锁定。

        总而言之,Qt 4 中的隐式共享类确实是隐式共享。即使在多线程应用程序中,您也可以像使用普通的、非共享的、基于值的可重入类一样安全地使用它们。

隐式共享的工作原理

  1. 共享数据块
    多个对象内部指向同一份数据块,数据块包含 数据内容 和 引用计数器

  2. 引用计数
    当新对象通过拷贝构造函数或赋值操作符创建时,引用计数加 1,不实际复制数据。

  3. 写时复制(COW)
    当某个对象尝试修改数据时,检查引用计数:

    • 若引用计数为 1(唯一所有者),直接修改数据。

    • 若引用计数大于 1(共享数据),创建数据的独立副本,修改副本,并将引用计数减 1。

隐式共享详解

如果对象即将发生变化且引用计数大于 1,隐式共享会自动将对象从共享块中分离。(这通常称为写时复制 或值语义)。

隐式共享类可控制其内部数据。在任何修改其数据的成员函数中,它都会在修改数据前自动脱离。不过,请注意容器迭代器的特殊情况;请参阅隐式共享迭代器问题。

使用隐式共享的QPen 类在所有修改内部数据的成员函数中都会脱离共享数据。

代码片段:

void QPen::setStyle(Qt::PenStyle style)
{detach();           // detach from common datad->style = style;   // set the style member
}void QPen::detach()
{if (d->ref != 1) {...             // perform a deep copy}
}

类列表

如果要更改对象,下面列出的类会自动从公共数据中分离出来。程序员甚至不会注意到这些对象是共享的。因此,应将它们的独立实例视为独立对象。它们的行为始终与独立对象无异,但却具有尽可能共享数据的额外好处。因此,您可以将这些类的实例作为参数按值传递给函数,而不必担心复制开销。

示例

QPixmap p1, p2;
p1.load("image.bmp");
p2 = p1;                        // p1 and p2 share dataQPainter paint;
paint.begin(&p2);               // cuts p2 loose from p1
paint.drawText(0,50, "Hi");
paint.end();

在本例中,p1 和p2 共享数据,直到QPainter::begin() 被调用为p2 ,因为绘制像素图会修改数据。

Implicit Sharing | Qt Core 6.8.2


http://www.ppmy.cn/embedded/176701.html

相关文章

java+selenium(资源全备,打开已使用浏览器信息,保留用户信息)

javaselenium(资源全备,打开已使用浏览器信息,保留用户信息) 一、介绍 我的代码可以实现以下效果: 保留用户信息,好处:可以在登录好一个账号后还保留原来的token验证信息 使用javaselenium实现爬取vue元素内容&…

接口自动化框架篇:自定义异常日志封装!

在接口自动化测试中,异常日志是非常重要的一部分。通过记录和封装异常日志,我们可以更好地定位和解决接口问题,提高测试效率和可维护性。本文将介绍如何从零开始,详细规范地编写自定义异常日志封装的接口自动化框架。 1. 确定异常…

流影---开源网络流量分析平台(一)(小白超详细)

目录 流影介绍 一、技术架构与核心技术 二、核心功能与特性 流影部署 流影介绍 一、技术架构与核心技术 模块化引擎设计 流影采用四层模块化架构:流量探针(数据采集)、网络行为分析引擎(特征提取)、威胁检测引擎&…

mysql传统主从模式下,主从中断接续

现象描述 传统模式的mysql主从。 Slave因为大事务延迟巨大。从库重启前的记录位点在binlog:552,pos:471157766 Relaylog:629,pos:496188584 从库重启后binlog倒退到221 Relaylog反而到了1653 故障判断 …

C++学习之QT中HTTP正则表达式

目录 1.知识点概述 2.BASE64介绍 3.QT中BASE64的使用 4.正则表达式中的位置限定字符 5.修饰数量的特殊字符 6.正则表达式中的字符匹配 7.正则表达式例子 8.正则表达式网址介绍 9.QNETWORKACCESSMANAGER类使用介绍 10.QNETWORKREQUEST类的使用 11.QNETWORKREPLY类的使…

clamav服务器杀毒(Linux服务器断网状态下如何进行clamav安装、查杀)

ClamAV服务器杀毒(服务器断网状态也可以使用该方法) 服务器因为挖矿病毒入侵导致断网,进行离线的clamav安装并查杀 安装包下载网址:https://www.clamav.net/downloads 安装.deb,如果服务器处于断网状态,可以…

Gitlab服务器数据迁移及版本升级

公司目前使用的GITLAB服务器,docker方式部署,GITLAB版本为13.11.0,由于版本太老存在安全漏洞,原服务器还部署了其他应用,不方便做升级操作,解决思路是将数据迁移新版本的gitlab服务器。 由于gitlab数据备份…

EasyUI数据表格中嵌入下拉框

效果 代码 $(function () {// 标记当前正在编辑的行var editorIndex -1;var data [{code: 1,name: 1,price: 1,status: 0},{code: 2,name: 2,price: 2,status: 1}]$(#dg).datagrid({data: data,onDblClickCell:function (index, field, value) {var dg $(this);if(field ! …