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 ...
随机推荐
- JS高阶---闭包面试题
[面试题1] 答案:The Window 分析: 本案例里,不存在闭包. 条件: .函数嵌套(满足) .内部函数调用外部函数变量(没有) 综上所述,该例中不存在闭包 [面试题2] 答案:My Obje ...
- 整合SpringTask实现定时任务
一.框架介绍 SpringTask是Spring自主研发的轻量级定时任务工具,相比于Quartz更加简单方便,且不需要引入其他依赖即可使用. 二.Corn表达式 概述 Cron表达式是一个字符串,包括 ...
- zz“深度高斯模型”可能为深度学习的可解释性提供概率形式的理论指导
[NIPS2017]“深度高斯模型”可能为深度学习的可解释性提供概率形式的理论指导?亚马逊机器学习专家最新报告 专知 [导读]在NIPS 2017上,亚马逊机器学习专家Neil Lawrence在12 ...
- LeetCode 1257. Smallest Common Region
原题链接在这里:https://leetcode.com/problems/smallest-common-region/ 题目: You are given some lists of region ...
- string方法介绍
#_*_coding:utf-8_*_#作者:王佃元#日期:2019/12/9#string操作print('hello'*2) #乘法操作,输出对应次数print('helloworld'[2:]) ...
- [LeetCode] 137. Single Number II 单独的数字之二
Given a non-empty array of integers, every element appears three times except for one, which appears ...
- Spring容器与SpringMVC容器的区别与联系
在spring整体框架的核心概念中,容器的核心思想是管理Bean的整个生命周期.但在一个项目中,Spring容器往往不止一个,最常见的场景就是在一个项目中引入Spring和SpringMVC这两个框架 ...
- ORA-12528: TNS:listener: all appropriate instances are blocking new connections
Oracle问题:ORA-12528: TNS: 监听程序: 所有适用例程都无法建立新连接 问题原始描述: ORA-12528: TNS:listener: all appropriate insta ...
- SQL Server 2014:为什么会提示“用户登录失败”?
SQL Server有两种登录方式,Windows身份验证和sql server身份验证,其对应的数据库连接字符串如下: Windows身份验证 ----- @"Data Source=DE ...
- SpringBoot 基于web应用开发(请求参数获取,静态资源,webjars)
SpringBoot 基于web应用开发 一.Lombok使用 1.导入依赖库 <dependency> <groupId>org.projectlombok</g ...