可恶的QT隐式共享
这个问题隐藏的很深,一般不容易察觉它造成的问题,而只是享受它提供的好处(节省内存,而且速度更快)。
但我发现它现在至少造成两个问题:
1. 把大量的QString放到QMap里,使用完毕后清空QMap,然而因为隐式共享的原因,实际上QString占用的大量内存得不到释放。这样程序积累了大量无用数据的内存,从此程序运行变得异常缓慢。
2. QFileInfo也有隐式共享问题,造成读取新文件信息的时候,估计要和旧文件信息全部对比一遍(就算是通过hash对比也很慢啊,我这里测试文件有11万个呢),确定没有这个新文件,然后才开始真正工作。这样效率极低。部分解决办法是使用const解决QFileInfo的问题。但是QString却不能使用const,因为我中间还要修改它。
只能慢慢研究了~
官方文档:
http://doc.qt.io/qt-4.8/implicit-sharing.html
http://doc.qt.io/qt-5/qstring.html
中文博客:
http://blog.csdn.net/yestda/article/details/17893221
http://blog.chinaunix.net/uid-27177626-id-3949985.html
http://blog.csdn.net/zhu_xz/article/details/6061201
------------------------------------------------------------------
隐式共享
说明对象在自操作前,指向一块共享的内存。
在自操作时,就会发生写时拷贝,让自己指向一块新的内存,这个内存也可以被其他对象引用,所以这个内存块有引用计数。当这块内存的引用计数为零时,会被回收。
所以,除非
QString *newString = new QString(theOldQString);
// then i forget to delete the newString
就会发生共享内存被引用但是却没被释放的问题。
其实隐式共享的内存确定可以被释放,但还是应用程序却仍然占据了高内存
这个就是你想要搞明白的问题
但是
for (int i=0; i<5000000; i++) {
QString str = "b";
list << str;
}
就不存在隐式共享的问题,会把整个对象放在list里
for (int i=0; i<5000000; i++) {
QString str = "aaaa";
list << str;
}
和
QString str = "aaaa";
for (int i=0; i<5000000; i++) {
list << str;
}
两段代码会使用完全不同的内存大小
------------------------------------------------------------------
for (int i=0; i<5000000; i++) {
QString str = "a";
list << str;
}
和
for (int i=0; i<5000000; i++) {
QString str = "aaaa";
list << str;
}
两段代码也会使用完全不同的内存大小,而且使用内存都特别大。
------------------------------------------------------------------
理论知识:
不同于 Java 风格遍历器,STL 风格遍历器直接指向元素本身。容器的begin()函数返回指向该容器第一个元素的遍历器;end()函数返回指向该容器最后一个元素之后的元素的遍历器。end()实际是一个非法位置,永远不可达。这是为跳出循环做的一个虚元素。如果集合是空的,begin()等于end(),我们就不能执行循环。
由于有隐式数据共享(我们会在后面的章节介绍该部分内容),即使一个函数返回集合中元素的值也不会有很大的代价。Qt API 包含了很多以值的形式返回QList或QStringList的函数(例如QSplitter::sizes())。如果你希望使用 STL 风格的遍历器遍历这样的元素,应该使用容器的拷贝,例如:
1
2
3
4
5
6
7
8
9
10
11
|
// 正确的方式
const QList<QString> sizes = splitter->sizes();
QList<QString>::const_iterator i;
for (i = sizes.begin(); i != sizes.end(); ++i)
...
// 错误的方式
QList<QString>::const_iterator i;
for (i = splitter->sizes().begin();
i != splitter->sizes().end(); ++i)
...
|
这个问题不存在于那些返回集合的 const 或非 const 引用的函数。隐式数据共享对 STL 风格遍历器造成的另外影响是,在容器上运行着非 const 遍历器的时候,不能对容器进行拷贝。Java 风格的遍历器没有这个问题。
foreach关键字
如果类型名中带有逗号,比如QPair<int, int="">,我们只能像上面一样,先创建一个对象,然后使用foreach关键字。如果没有逗号,则可以直接在foreach关键字中使用新的对象。
Qt 会在foreach循环时自动拷贝容器。这意味着,如果在遍历时修改集合,对于正在进行的遍历是没有影响的。即使不修改容器,拷贝也是会发生的。但是由于存在隐式数据共享,这种拷贝还是非常迅速的。
因为foreach创建了集合的拷贝,使用集合的非 const 引用也不能实际修改原始集合,所修改的只是这个拷贝。
参考:http://jukezhang.com/2014/11/23/learn-qt-eight/
参考:http://www.devbean.net/2013/01/qt-study-road-2-implicit-sharing/
http://www.devbean.net/2013/01/qt-study-road-2-iterator/
可恶的QT隐式共享的更多相关文章
- Qt隐式共享机制
1.浅拷贝 浅拷贝-引用类型.浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同),对其中任何一个对象的改动都会影响另外一个对象. 2.深拷贝 而深拷贝-值类型.深拷贝是指源对象与 ...
- Qt隐式共享与显式共享
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Amnes1a/article/details/69945878Qt中的很多C++类都使用了隐式数据共 ...
- 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 ...
随机推荐
- jQuery导航菜单防刷新
为了实现最主要的功能,只写了一个粗糙的案例 CSS样式 ul,li{ list-style-type:none;} .nav { width: 100%; height: 35px; line-hei ...
- [转]SharePoint 2010 Download as Zip File Custom Ribbon Action
在SharePoint 2010文档库中,结合单选框,在Ribbon中提供了批量处理文档的功能,比如,批量删除.批量签出.批量签入等,但是,很遗憾,没有提供批量下载,默认的只能一个个下载,当选择多个文 ...
- MVC中的URL路由(一)
URL路由系统通过对请求地址进行解析从而得到以目标Controller名称为核心的路由数据.Url路由系统最初是为了实现请求url与物理文件路径分离而建立的,MVC的Url Route是将Url地址与 ...
- 怎样区分JQuery对象和Dom对象 常用的写法
第一步,http://www.k99k.com/jQuery_getting_started.html 第二步,新手先仔细得全部看一遍jQuery的选择器,很重要!!! (http://shawphy ...
- initialize 和init
initialize 是类方法,创建实例时会调用该方法.但是只会调用一次.如一个类创建了10个对象,initialize方法只会调用一次,但是init会调用10次.init 是实例方法,每次创建一个实 ...
- frame 第三节
1.准备3个文件 main.html: <html> <head> <title>框架</title> </head> <frames ...
- SGU 解题报告
Volume 1 Volume 2
- SGU 218.Unstable Systems
题意: 有n(n<500)台机器,和500个程序.不同的程序在不同的机器上运行有着不同的不稳定度s[i][j].求最小的最大稳定度及其方案. Solution: 比较经典的二分图模型. 建图很简 ...
- nginx 显示discuz页面
我也不知道我题目表述的对不对.我也是刚学习. 安装nginx 显示discuz的界面.我看了好几次视频,也没发现哪的问题.就是一直出现502的错误代码 我又重新查了几次.觉得需要注意一下几点: 1,关 ...
- 更新ACCESS数据库出现“字段太小而不能接受所要添加的数据的数量。试着插入或粘贴较少的数据。”的解决方法
今天进行数据调试时出现“字段太小而不能接受所要添加的数据的数量.试着插入或粘贴较少的数据.”,跟踪发现是在更新数据库的数据时出现的. 打开数据库表格发现出错的数据字段类型被定义为“文本”,也就是数据最 ...