foreach、qAsConst用法总结
預備知識
本文要用到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用法总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTC 为 VIVE XR 精英套装推出
- 下一篇: vivo推出“手语翻译官”应用:准确率可