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