IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Qt隐式共享detach函数的理解 -> 正文阅读

[C++知识库]Qt隐式共享detach函数的理解

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

以下是本人的理解:

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

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

创建共享对象时,它将引用计数设置为 1。每当新对象引用共享数据时,引用计数就会增加,当对象取消引用共享数据时,引用计数会减少。当引用计数变为零时,共享数据将被删除。

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

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

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

刚new出来的QPen对象被pPen1更改了,此时pPen2的调用方也跟着变化了,此时pPen2的调用方
功能就会不正确,就会感觉诡异,心里暗想:我明明啥都没改,怎么画笔就变了呢?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的对象被更改。
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-09 12:22:52  更:2022-05-09 12:23:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 0:18:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码