QT容器遍歷分為Java和STL遍歷
STL風(fēng)格遍歷器的語(yǔ)法類似于使用指針對(duì)數(shù)組的操作。我們可以使用++和--運(yùn)算符使遍歷器移動(dòng)到下一位置,遍歷器的返回值是指向這個(gè)元素的指針。例如QVector<T>的iterator返回值是T *類型,而const_iterator返回值是const T *類型(數(shù)據(jù)為常量,返回值不能修改)。
一個(gè)典型的使用STL風(fēng)格遍歷器的代碼是:
QList<double>::iterator i = list.begin();
while (i != list.end()) {
*i = qAbs(*i);
++i;
}
對(duì)于某些返回容器的函數(shù)而言,如果需要使用STL風(fēng)格的遍歷器,我們需要建立一個(gè)返回值的拷貝,然后再使用遍歷器進(jìn)行遍歷。如下面的代碼所示:
QList<int> list = splitter->sizes();
QList<int>::const_iterator i = list.begin();
while (i != list.end()) {
doSomething(*i);
++i;
}
在C++中,很多人都會(huì)說(shuō),要避免這么寫,因?yàn)樽詈笠粋€(gè)return語(yǔ)句會(huì)進(jìn)行臨時(shí)對(duì)象的拷貝工作。如果這個(gè)對(duì)象很大,這個(gè)操作會(huì)很昂貴。所以,資深的C++高手們都會(huì)有一個(gè)STL風(fēng)格的寫法:
void sineTable(std::vector<double> &vect)
{
vect.resize(360);
for (int i = 0; i < 360; ++i)
vect[i] = std::sin(i / (2 * M_PI));
}
// call
QVector<double> v;
sineTable(v);
這種寫法通過傳入一個(gè)引用避免了拷貝工作。但是這種寫法就不那么自然了。而隱式數(shù)據(jù)共享的使用讓我們能夠放心的按照第一種寫法書寫,而不必?fù)?dān)心性能問題。
Qt所有容器類以及其他一些類都使用了隱式數(shù)據(jù)共享技術(shù),這些類包括QByteArray, QBrush, QFont, QImage, QPixmap和QString。這使得這些類在參數(shù)和返回值中使用傳值方式相當(dāng)高效。
不過,為了正確使用隱式數(shù)據(jù)共享,我們需要建立一個(gè)良好的編程習(xí)慣。這其中之一就是,對(duì)list或者vector使用at()函數(shù)而不是[]操作符進(jìn)行只讀訪問。原因是[]操作符既可以是左值又可以是右值,這讓Qt容器很難判斷到底是左值還是右值,而at()函數(shù)是不能作為左值的,因此可以進(jìn)行隱式數(shù)據(jù)共享。另外一點(diǎn)是,對(duì)于begin(),end()以及其他一些非const容器,在數(shù)據(jù)改變時(shí)Qt會(huì)進(jìn)行深復(fù)制。為了避免這一點(diǎn),要盡可能使用const_iterator, constBegin()和constEnd().
最后,Qt提供了一種不使用遍歷器進(jìn)行遍歷的方法:foreach循環(huán)。這實(shí)際上是一個(gè)宏,使用代碼如下所示:
QLinkedList<Movie> list;
Movie movie;
...
foreach (movie, list) {
if (movie.title() == "Citizen Kane") {
std::cout << "Found Citizen Kane" << std::endl;
break;
}
}
Qt容器類之關(guān)聯(lián)存儲(chǔ)容器
Qt提供兩種關(guān)聯(lián)容器類型:QMap<K, T>和QHash<K, T>。
QMap<K, T>是一種鍵-值對(duì)的數(shù)據(jù)結(jié)構(gòu),它實(shí)際上使用跳表skip-list實(shí)現(xiàn),按照K進(jìn)行升序的方式進(jìn)行存儲(chǔ)。使用QMap<K, T>的insert()函數(shù)可以向QMap<K, T>中插入數(shù)據(jù),典型的代碼如下:
QMap<QString, int> map;
map.insert("eins", 1);
map.insert("sieben", 7);
map.insert("dreiundzwanzig", 23);
同樣,QMap<K, T>也重載了[]運(yùn)算符,你可以按照數(shù)組的復(fù)制方式進(jìn)行使用:
map["eins"] = 1;
map["sieben"] = 7;
map["dreiundzwanzig"] = 23;
int val = map.value("dreiundzwanzig");
遍歷關(guān)聯(lián)存儲(chǔ)容器的最簡(jiǎn)單的辦法是使用Java風(fēng)格的遍歷器。因?yàn)?code>Java風(fēng)格的遍歷器的next()和previous()函數(shù)可以返回一個(gè)鍵-值對(duì),而不僅僅是值,例如:
QMap<QString, int> map;
...
int sum = 0;
QMapIterator<QString, int> i(map);
while (i.hasNext())
sum += i.next().value();
如果我們并不需要訪問鍵-值對(duì),可以直接忽略next()和previous()函數(shù)的返回值,而是調(diào)用key()和value()函數(shù)即可,如:
QMapIterator<QString, int> i(map);
while (i.hasNext()) {
i.next();
if (i.value() > largestValue) {
largestKey = i.key();
largestValue = i.value();
}
}
Mutable遍歷器則可以修改key對(duì)應(yīng)的值:
QMutableMapIterator<QString, int> i(map);
while (i.hasNext()) {
i.next();
if (i.value() < 0.0)
i.setValue(-i.value());
}
如果是STL風(fēng)格的遍歷器,則可以使用它的key()和value()函數(shù)。而對(duì)于foreach循環(huán),我們就需要分別對(duì)key和value進(jìn)行循環(huán)了:
QMultiMap<QString, int> map;
...
foreach (QString key, map.keys()) {
foreach (int value, map.values(key)) {
doSomething(key, value);
}
}