Qt隐式共享机制
1、浅拷贝
浅拷贝-引用类型。浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同),对其中任何一个对象的改动都会影响另外一个对象。
2、深拷贝
而深拷贝-值类型。深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
3、隐式共享:
隐式共享又叫做回写复制。当两个对象共享同一份数据时(通过浅拷贝实现数据块的共享),如果数据不改变,不进行数据的复制。而当某个对象需要改变数据时则执行深拷贝。
采用隐式共享技术,将深拷贝和浅拷贝有机地结合起来。
Qt中许多常用的类都使用了隐式共享技术,如QString、QImage、容器类、绘图相关类等等。
|
1
2 3 4 5 |
QString str1 = "ubuntu";
QString str2 = str1; //str2 = "ubuntu" ] = "m"; //str2 = "ubmntu",str1 = "ubuntu" ] = "o"; //str2 = "obmntu",str1 = "ubuntu" str1 = str2; //str1 = "obmntu", |
解释:
line1: 初始化一个内容为"ubuntu"的字符串;
line2: 将字符串对象str1赋值给另外一个字符串str2(由QString的拷贝构造函数完成str2的初始化)。
在对str2赋值的时候,会发生一次浅拷贝,导致两个QString对象都会指向同一个数据结构。该数据结构除了保存字符串“ubuntu”之外,还保存一个引用计数器,用来记录字符串数据的引用次数。此处,str1和str2都指向同一数据结构,所以此时引用计数器的值为2.
line3: 对str2做修改,将会导致一次深拷贝,使得对象str2指向一个新的、不同于str1所指的数据结构(该数据结构中引用计数器值为1,只有str2是指向该结构的),同时修改原来的、str1所指向的数据结构,设置它的引用计数器值为1(此时只有str1对象指向该结构);并在这个str2所指向的、新的数据结构上完成数据的修改。引用计数为1就意味着该数据没有被共享。
line4: 进一步对str2做修改,不过不会引起任何形式的拷贝,因为str2所指向的数据结构没有被共享。
line5: 将str2赋给str1.此时,str1修改它指向的数据结构的引用计数器的值位0,表示没有QString类的对象再使用这个数据结构了;因此str1指向的数据结构将会从从内存中释放掉;这一步操作的结构是QString对象str1和str2都指向了字符串为“obmntu”的数据结构,该结构的引用计数为2。
|
1
2 3 4 5 6 7 8 9 10 11 12 13 |
void QPen::setStyle(Qt::PenStyle style)
{ detach(); // detach from common data d->style = style; // set the style member } void QPen::detach() |
使用隐式共享的QPen类与更改内部数据的所有成员函数中的共享数据分离。
隐式共享主要发生在幕后; 程序员很少需要担心它。 但是,Qt的容器迭代器具有与STL不同的行为。
隐式共享对STL样式的迭代器有另一个影响:当迭代器在该容器上处于活动状态时,应避免复制容器。 迭代器指向内部结构,如果复制容器,则应该非常小心迭代器。 例如:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
QVector<int> a, b;
a.resize(); // make a big vector filled with 0. QVector<int>::iterator i = a.begin(); a[; b.clear(); // Now the iterator i is completely invalid. int j = *i; // Undefined behavior! |
4、自定义隐式共享类
实现自己的隐式共享类时,请使用QSharedData和QSharedDataPointer类。
下面,我们以一个员工类为例,来实现一个隐式共享类。步骤如下:
定义类Emplyee,该类只有一个唯一的数据成员,类型为QSharedDataPointer<EmployeeData>。
定义类EmployeeData类,其派生自QSharedData。该类中包含的就是原本应该放在Employee类中的那些数据成员。
类定义如下:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include <QSharedData>
#include <QString> class EmployeeData : public QSharedData int id; class Employee int id() const private: |
解释:
在Employee类中,要注意这个数据成员d。所有对employee数据的访问都必须经过d指针的operator->()来操作。对于写访问,operator->()会自动的调用detach(),来创建一个共享数据对象的拷贝,如果该共享数据对象的引用计数大于1的话。也可以确保向一个Employee对象执行写入操作不会影响到其他的共享同一个EmployeeData对象的Employee对象。
类EmployeeData继承自QSharedData,它提供了幕后的引用计数。
在幕后,无论何时一个Employee对象被拷贝、赋值或作为参数传,QSharedDataPointer会自动增加引用计数;无论何时一个Employee对象被删除或超出作用域,QSharedDataPointer会自动递减引用计数。当引用计数为0时,共享的EmployeeData对象会被自动删除。
void setId(int id) { d->id = id; }
void setName(const QString &name) { d->name = name; }
在Employee类的非const成员函数中,无论何时d指针被解引用,QSharedDataPointer都会自动的调用detach()函数来确保该函数作用于一个数据拷贝上。并且,在一个成员函数中,如果对d指针进行了多次解引用,而导致调用了多次detach(),也只会在第一次调用时创建一份拷贝。
int id() const { return d->id; }
QString name() const { return d->name; }
但在Employee的const成员函数中,对d指针的解引用不会导致detach()的调用。
还有,没必要为Employee类实现拷贝构造函数或赋值运算符,因为C++编译器提供的拷贝构造函数和赋值运算符的逐成员拷贝就足够了。
因为,我们唯一需要拷贝的就是d指针,而该指针是一个QSharedDataPointer,它的operator=()仅仅是递增了共享对象EmployeeData的引用计数。


如果要使用显式共享,请使用QExplicitySharedDataPointer。
5、总结
最大化资源有效利用,最小化复制克隆操作。
深入理解:
https://doc.qt.io/qt-5/implicit-sharing.html
Qt隐式共享机制的更多相关文章
- Qt隐式共享与显式共享
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Amnes1a/article/details/69945878Qt中的很多C++类都使用了隐式数据共 ...
- 可恶的QT隐式共享
这个问题隐藏的很深,一般不容易察觉它造成的问题,而只是享受它提供的好处(节省内存,而且速度更快). 但我发现它现在至少造成两个问题: 1. 把大量的QString放到QMap里,使用完毕后清空QMap ...
- Qt——数据的隐式共享
一.隐式共享类 在Qt中有很多隐式共享类( Implicitly Shared Classes ),什么是隐式共享呢,请参考官方文档的说明. 好吧,翻译一下—— 许多C++类隐式地共享数据,使得资源使 ...
- C++中的深拷贝和浅拷贝 QT中的深拷贝,浅拷贝和隐式共享
下面是C++中定义的深,浅拷贝 当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用.也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用.以下情况都会 ...
- 关于QT中的隐式共享
网上关于隐式共享的解释很多,在此不再陈述.本文主要是记录一下自己学习隐式共享的坑点: 即:隐式共享只发生在非指针的情况下!!!! 如下代码: QImage image1; QImage image2; ...
- QVector 和vector的比较(QVector默认使用隐式共享,而且有更多的函数提供)
QVector和vector的比较: Qvector默认使用隐式共享,可以用setSharable改变其隐式共享.使用non-const操作和函数将引起深拷贝.at()比operator[](),快, ...
- 01Qt中的隐式共享
隐式共享 隐式共享又称为回写复制(copy on write).当两个对象共享同一分数据时(通过浅拷贝实现数据共享),如果数据不改变,则不进行数据的复制.而当某个对象需要需要改变数据时,则进行深拷 ...
- QT隐式数据共享
QT中许多C++类使用了隐式数据共享,最小化资源拷贝.当作为参数传递时,实际只传递了指针,这是底层完成的,程序员无需担心,即使是在多线程中,从Qt4开始: 记住,尽量使用const迭代器,vector ...
- QPointer更安全,QScopedPointer自动出范围就删除,QSharedDataPointer帮助实现隐式共享
http://blog.csdn.net/hai200501019/article/details/8474582http://blog.csdn.net/hai200501019/article/d ...
随机推荐
- Nginx配置参数中文详细说明
#定义Nginx运行的用户和用户组user www www;##nginx进程数,建议设置为等于CPU总核心数.worker_processes 8;##全局错误日志定义类型,[ debug | in ...
- 莫烦TensorFlow_02 Session的两种方法
import tensorflow as tf matrix1 = tf.constant([[3,3]]) # 1X2 matrix2 = tf.constant([[2], [2]]) produ ...
- 卷积,reLu,池化的意义
1.卷积 提取局部特征 2.Relu 留下相关特征,去掉不相关特征,卷积之后的正值越大,说明与卷积核相关性越强,负值越大,不相关性越大. 3.池化 池化的目的: (1)留下最相关的特征,或者说留下最明 ...
- maven warnning 'build.plugins.plugin.version' is missing
裝完maven后,package或clean时出错:[WARN] [WARN] Some problems were encountered while building the effective ...
- [LeetCode] 45. Jump Game II 跳跃游戏之二
Given an array of non-negative integers, you are initially positioned at the first index of the arra ...
- 2019 SDN上机第1次作业
一.安装轻量级网络仿真工具Mininet 克隆github上的Mininet源 git clone https://github.com/mininet/mininet 选择默认全部安装 cd min ...
- Spring Cloud Alibaba整合Sentinel流控
前面我们都是直接通过集成sentinel的依赖,通过编码的方式配置规则等.对于集成到Spring Cloud中阿里已经有了一套开源框架spring-cloud-alibaba,就是用于将一系列的框架成 ...
- Eclipse:设置自动补全,提高编程效率
一.设置自动补全 1.进入eclipse的window里的perferences页面 2.找到java->Editor->Content Assist设置界面 3.在Auto activa ...
- java通过maven打包解决xml读取格式失败问题
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compi ...
- JVM学习笔记1
1.运行时数据结构 2.堆分代 3.垃圾收集器 Parallel Scavenge收集器:新生代称为PSYoungGen,老年代称为ParOldGen,永久代称为Metaspace ParNew收集器 ...