预备知识

本文要用到Qt的detach、隐式共享技术,如果对这两种技术不了解的读者,请在qt官方自带的Assistant 中的“索引”tab页的搜索框中输入implicitly shared,就可以看到Qt官方的阐述。

foreach说明

foreach关键字是Qt中用于遍历容器的一个关键字,是Qt官方自己实现的,其不是C++标准中存在的关键字。其语法如下:

foreach (variable, container)

利用foreach可以对Qt自己的容器如:QVector、QMap、 QHash、QLinkedList、QList进行遍历,如下:

QLinkedList<QString> list;...foreach (const QString &str, list) {if (str.isEmpty())break;qDebug() << str;}

对于QMap、 QHash键值对类型遍历如下:

    QMap<int, QString> mpStudent;mpStudent[0] = "dan";mpStudent[1] = "shi";mpStudent[2] = "ming";foreach(auto var, mpStudent){qDebug() << var<< "\r\n";}

结果如下:

可以看到,将foreach运用于QMap、QHash遍历时,默认返回QMap、QHash键值对的值部分。如果要遍历QMap、QHash的键部分,可将foreach语句改为如下:

foreach(auto var, mpStudent.keys())

如果要遍历键又要遍历值,则请用迭代器。

foreach也可以遍历STL的容器,如下:

    vector<int>vtSTLTest;for (auto i = 0; i < 10; ++i){vtSTLTest.push_back(i);}foreach(auto var, vtSTLTest){qDebug() << var << "\r\n";}

Qt进入到foreach循环后,会将容器自动拷贝一份,因为Qt的容器都是隐式共享的(类似于智能指针),所以拷贝Qt自己的容器过程非常快,几乎对性能没啥大的影响,但因为STL的容器没有像Qt自己的容器那样实现隐式共享,所以如果拷贝的是STL容器,有时代价是昂贵的。因为foreach自动拷贝了原来的容器,所以在foreach循环内,对容器的更改只会影响到拷贝的容器,对于原始容器则不会有影响,基于这一点,在foreach语法中,variable不能为非常量的引用,只能为值传递或常量引用,否则就可以对原始容器进行更改了,因此下面的语法,编译器都会报错

   vector<int>vtSTLTest;for (auto i = 0; i < 10; ++i){vtSTLTest.push_back(i);}foreach(auto& var, vtSTLTest){var[1] = 2;qDebug() << var << "\r\n";}

试图修改原始容器索引为1的元素,会报错

    vector<int>vtSTLTest;for (auto i = 0; i < 10; ++i){vtSTLTest.push_back(i);}foreach(int& var, vtSTLTest){qDebug() << var << "\r\n";}

非常量的int引用会导致原始容器被修改,所以会报错。

foreach(int& var, vtSTLTest)

将上面一句代码改为:

foreach(const int& var, vtSTLTest)

或改为:

foreach(int var, vtSTLTest)

不会报错,这种情况下,不会导致对原始容器更改。

如果在用foreach遍历容器时,外部更改了原始容器,则它不会影响foreach循环,即foreach循环输出的依然是原始容器未修改的值,这证明foreach确实复制了原始容器,操作的是复制容器而不是原始容器。如下:

#include "QtWidgetsApplication2.h"
#include <QVector>
#include <map>
#include <thread>
using namespace  std;void fun(QVector<int>& vtQTest)
{for (auto i = 0; i < 10; ++i){vtQTest[i] = i+2;}
}QtWidgetsApplication2::QtWidgetsApplication2(QWidget *parent): QWidget(parent)
{ui.setupUi(this);QVector<int>vtQTest;for (auto i = 0; i < 10; ++i){vtQTest.push_back(i);}std::thread* p{nullptr};foreach ( int var ,vtQTest){if (nullptr == p){p = new std::thread(fun, std::ref(vtQTest));// 等待5秒,以便原始容器内的值能被线程全部更改完std::this_thread::sleep_for(std::chrono::milliseconds(5000)); }// 虽然上面线程修改了原始容器,但这里依然输出的是未修改的值qDebug() << var << "\r\n";}foreach(int var, vtQTest)  {// 进入到这个foreach,会重新拷贝一份vtQTest,而vtQTest在上面的线程中// 被更改了,所以这里输出的更改后的值。qDebug() << "new :" << var << "\r\n";}}

输出如下:

基于范围的循环可能导致Qt库容器会执行detach操作,如下:

QString s = ...;for (QChar ch : s) // detaches 's' (performs a deep-copy if 's' was shared)process(ch);

上述代码是用for实现的基于范围的循环,符合C++11标准语法。当process函数对ch进行更改操作之前,s就会被detach,之后再深拷贝出一个和s一样的对象,而有时候我们不想被detach和深拷贝。但换成如下的foreach,则s不会被detach,仅仅进行指针级的浅拷贝:

      QString s = ...;foreach (QChar ch, s)  process(ch);

但是在STL的容器上用foreach,却会导致复制操作,前文已经说过,STL的容器没有实现隐式共享,这有时会导致复制的代价很高,对性能和效率有影响。为了兼顾这两者,Qt官方给出了如下建议:

  • 对于Qt自己实现的容器,如:QVector、QMap、 QHash、QLinkedList、QList等,建议用foreach进行循环。
  • 对于STL的容器,建议用for(var : container)基于范围的循环。

qAsConst

自Qt 5.7版本以来,引入了qAsConst函数,该函数Qt官方的解释如下:

  • This function is a Qt implementation of C++17's std::as_const(),this function turns non-const lvalues into const lvalues
  • Added qAsConst function to help using non-const Qt containers in C++11 range for loops
  • Its main use in Qt is to prevent implicitly-shared Qt containers from detaching

意思就是说:

  • 这个函数实现了C++17标准中的std::as_const()函数的功能,将一个非常量的左值转为常量的左值。
  • 增加qAsConst函数是为了Qt自己的容器能实现C++11标准的基于范围的循环。
  • 该函数主要用于qt容器在隐式共享中不被detach。

从上文的描述,我们知道下面的代码:

QString s = ...;for (QChar ch : s) // detaches 's' (performs a deep-copy if 's' was shared)process(ch);

将会导致s被detach,继而再执行深拷贝,而下面的代码s不会被detach,当然也就不会再执行深拷贝了

 for (QChar ch : qAsConst(s)) // ok, no detach attemptprocess(ch);

当然,在这种情况下,你也许会说,像下面那样将s声明为const,也不会执被detach:

const QString s = ...;for (QChar ch : s) // ok, no detach attempt on const objectsprocess(ch);

但是在编程时、在现实中,声明为const往往不容易做到。

总结:对Qt自己实现的容器如:QVector、QMap、 QHash、QLinkedList、QList等,如果一定要用基于for(var : container)范围的循环,则请用如下形式:

for(var : qAsConst(container))

foreach、qAsConst用法总结相关推荐

  1. php foreach嵌套foreach,php中foreach怎么嵌套foreach PHP中foreach函数用法?

    foreach的使用方法小编不是很明确,分享达人指教一下.foreach (array_expressforeach($array as $key) { if(xxxx) { break; //bre ...

  2. php foreach是什么,php中foreach的用法是什么

    php中foreach的用法是:[foreach ($array as $value) {要执行代码;}],每进行一次循环,当前数组元素的值就会被赋值给$value变量,在进行下一次循环时,将得到数组 ...

  3. TCL foreach的用法

    文章目录 foreach var list body foreach var1 list1 ?var2 list2 var3 list3 ...? body 本篇文章介绍的是foreach的用法,fo ...

  4. php的foreach什么意思,php中foreach的用法是什么,php foreach as

    php中foreach的用法是什么PHP中foreach的用法是什么,foreach在php中的用法是:[foreach($ array as $ value){要执行的代码:}],对于每个循环,当前 ...

  5. php foreach详解,php foreach的用法详解,foreach怎么使用

    php foreach的用法详解详细解释了PHP foreach的用法,php foreach的用法:1.通过语法"foreach(array _ expression as $ value ...

  6. c语言foreach函数,c中foreach的用法

    下面小编就跟你们详细介绍下c中foreach的用法的用法,希望对你们有用. c中foreach的用法的用法如下: c:forEach>用法  博客分类: ?J2EE CC++C#JSPSQL 为 ...

  7. jstl标签forEach的用法--遍历java的集合

    再讲<c:forEach>之前,现讲一下让EL表达式生效的语句 <% @ page isELIgnored="false"%>这句语句在你想让EL表达式生效 ...

  8. php中foreach()的用法

    foreach()有两种用法: 1: foreach(array_name as $value)    {       statement;    } 这里的array_name是你要遍历的数组名,每 ...

  9. foreach的用法有哪些呢?

    电脑和手机等等一系列电子产品成为了我们生活中不可缺少的一部分,JAVA便成为了人们在生活中常常用到的知识,那么foreach语句有哪些用法呢? foreach语句的用法 foreach是JAVA中的一 ...

最新文章

  1. 人生致命的8个经典问题[转]
  2. IDT 信息设计工具使用
  3. 复习(二)—— Makefile工具使用
  4. HTTPS是如何加密的
  5. 在Spring data中使用r2dbc
  6. 存储过程实现可扩展灵活接口
  7. arduino智能浇花系统_创新成果 | 养花神器——智能浇花机
  8. Atitit.ide eclipse编译速度慢的解决
  9. 抖音python上的代码_抖音代码舞「图文推荐」,python实例代码
  10. python代码怎么保存为pdf_如何在Selenium(Python)中将打开的页面保存为pdf
  11. ubuntu 安装python mysqldb
  12. 圆锥螺旋线matlab 画,圆锥螺旋线 - calculus的日志 - 网易博客
  13. 如何制定一个App推广活动方案
  14. 数组和字符串赋值的问题(定义时不初始化)
  15. matplotlib之箱型图
  16. The c program language 1
  17. php 生成8位数唯一的激活码
  18. C++题目:新的篮球队(题集)
  19. 数据仓库架构/流程图
  20. Elasticsearch宕机问题

热门文章

  1. 怎么用计算机算p a,老师,(P/A,12%,10)这个值用计算器怎么算出来?
  2. 双向链表操作c语言 函数,c语言 双向链表的基础操作
  3. 十大笔记本电脑排行_十大笔记本电脑品牌排行榜 世界上最受欢迎的电脑品牌...
  4. Android开发的小技巧总结
  5. HDU3247 Resource Archiver(AC自动机+BFS+DP)
  6. 多种冒泡算法时间和效率比较
  7. MFC和QT等UI框架的特点
  8. MFC+OPENCV+显示MAT类型图像
  9. C#中利用委托实现多线程跨线程操作
  10. C#语言连接Mysql数据库实现增删改查