Qt 隐式共享

ops/2025/3/26 1:52:46/

隐性共享

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/ops/169803.html

相关文章

FFmpeg + ‌Qt‌ 简单视频播放器代码

一个基于 ‌FFmpeg 4.x‌ 和 ‌Qt‌ 的简单视频播放器代码示例,实现视频解码和渲染到 Qt 窗口的功能。 1)ffmpeg库界面,视频解码支持软解和硬解方式。 2)QImage/QPixmap显示视频图片。 ‌1. Qt 项目配置(.pro 文件&…

chrome插件开发之API解析-chrome.tabs.query

chrome.tabs.query 是 Chrome 扩展开发中用于查询浏览器标签页信息的 API。它允许你根据指定的条件获取当前浏览器中所有匹配的标签页。这个 API 返回一个 Promise,解析后会得到一个包含匹配标签页信息的数组。 常见用途 获取当前活动标签页:可以获取当…

LeetCode hot 100 每日一题(15)——48.旋转图像

这是一道难度为中等的题目,让我们来看看题目描述: 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 提示…

“张江引擎 人形启程”,AI 机器人开启上海进化新范式

当AI从虚拟算法跃入物理世界,机器人正以技术攻关、产品迭代、场景落地为着力点,为上海塑造现代化产业体系提供重要支撑。 在张江机器人谷这片创新热土上,青龙机械臂在产线精准起舞,开普勒物流机器人走出仓库化身“物流小哥”&…

Jupyter Notebook 常用命令(自用)

最近有点忘记了一些常见命令,这里就记录一下,懒得找了。 文章目录 一、文件操作命令1. %cd 工作目录2. %pwd 显示路径3. !ls 列出文件4. !cp 复制文件5. !mv 移动或重命名6. !rm 删除 二、代码调试1. %time 时间2. %timeit 平均时长3. %debug 调试4. %ru…

计算机网络 第一章:计算机网络和因特网(2)

1.4 分组交换网中的时延、丢包和吞吐量 1.4.1 分组交换网中的时延概述 分组从一台主机(源)出发,通过一系列路由器传输,在另一台主机(目的地)中结束它的历程。当分组从一个节点(主机或路由器&…

提取关键 CSS: react 的项目中如何使用criticalCSS

在 React 项目中优化首屏加载性能时,Critical CSS(关键 CSS)是一项重要技术。以下是详细步骤和实现方法: 一、Critical CSS 的作用 首屏优化:提取渲染首屏内容所需的最小 CSS,内联到 HTML 中,减…

C++:背包问题习题

1. 货币系统 1371. 货币系统 - AcWing题库 给定 V 种货币(单位:元),每种货币使用的次数不限。 不同种类的货币,面值可能是相同的。 现在,要你用这 V 种货币凑出 N 元钱,请问共有多少种不同的…