概述
對于每個容器類,都有兩種stl風格的迭代器類型:一種提供只讀訪問,另一種提供讀寫訪問。應該盡可能使用只讀迭代器,因為它們比讀寫迭代器快。
STL迭代器的API以數組中的指針為模型。例如,++操作符將迭代器推進到下一項,*操作符返回迭代器指向的項。實際上,對于QVector和QStack(它們將元素存儲在相鄰的內存位置),迭代器類型只是T *的一個typedef,而const_iterator類型只是const T *的一個typedef。
示例
在本文檔中,將集中討論QList和QMap。QLinkedList、QVector和QSet的迭代器類型與QList的迭代器具有完全相同的接口;類似地,QHash的迭代器類型與QMap的迭代器具有相同的接口。
下面是按順序遍歷QList<QString>
中的所有元素并將它們轉換為小寫的示例:
QList<QString> list;list << "A" << "B" << "C" << "D";QList<QString>::iterator i;for (i = list.begin(); i != list.end(); ++i)*i = (*i).toLower();
與java風格的迭代器不同,stl風格的迭代器直接指向項。容器的begin()函數返回一個迭代器,該迭代器指向容器中的第一項。容器的end()函數返回一個迭代器,指向容器中最后一項后面一個位置的虛項。End()標記一個無效的位置;它永遠不能被取消引用。它通常用于循環的中斷條件。如果列表為空,begin()等于end(),因此我們永遠不會執行循環。
下圖用紅色箭頭顯示了包含四個元素的vector的有效迭代器位置:
使用stl風格的迭代器進行向后迭代可以使用反向迭代器完成:
QList<QString> list;list << "A" << "B" << "C" << "D";QList<QString>::reverse_iterator i;for (i = list.rbegin(); i != list.rend(); ++i)*i = i->toLower();}
在到目前為止的代碼片段中,使用一元*
操作符檢索存儲在某個迭代器位置的項(QString類型),然后對其調用QString::toLower()
。大多數c++編譯器也允許編寫i->toLower()
,但有些編譯器不允許。
對于只讀訪問,可以使用const_iterator
、constBegin()
和constEnd()
。例如:
QList<QString>::const_iterator i;for (i = list.constBegin(); i != list.constEnd(); ++i)qDebug() << *i;
stl風格API
下表總結了stl
風格迭代器的API:
方法 | 行為 |
---|---|
*i | 返回當前項。 |
++i | 將迭代器推進到下一項 |
i += n | 將迭代器向前移動n個元素 |
–i | 將迭代器向后移動一項 |
i -= n | 將迭代器向后移動n個元素 |
i - j | 返回迭代器i和j之間的項數 |
++
和--
操作符都可以作為前綴(++i
,--i
)和后綴(i++
,i--
)操作符使用。前綴版本修改迭代器并返回對修改后迭代器的引用;后綴版本在修改迭代器之前獲取該迭代器的副本,并返回該副本。在忽略返回值的表達式中,我們建議您使用前綴操作符(++i
,--i
),因為這些操作符略快一些。
對于非常量迭代器類型,一元*
操作符的返回值可用于賦值操作符的左側。
對于QMap
和QHash
, *
操作符返回項的值組件。如果要檢索鍵,請在迭代器上調用key()
。為了對稱,迭代器類型還提供了一個value()
函數來檢索值。例如,下面是將QMap
中的所有項目打印到控制臺:
QMap<int, int> map;...QMap<int, int>::const_iterator i;for (i = map.constBegin(); i != map.constEnd(); ++i)qDebug() << i.key() << ':' << i.value();
由于隱式共享,函數為每個值返回一個容器的成本非常低。Qt API包含幾十個函數,每個值返回一個QList或QStringList(例如,QSplitter::sizes())。如果希望使用STL迭代器遍歷這些對象,則應該始終獲取容器的一個副本,并遍歷該副本。例如:
// RIGHTconst QList<int> sizes = splitter->sizes();QList<int>::const_iterator i;for (i = sizes.begin(); i != sizes.end(); ++i)...// WRONGQList<int>::const_iterator i;for (i = splitter->sizes().begin();i != splitter->sizes().end(); ++i)...
對于返回const或非const對容器的引用的函數,不會出現此問題。
隱式共享迭代器問題
隱式共享對于stl風格的迭代器還有另一個后果:當迭代器在容器上處于活動狀態時,應該避免復制容器。迭代器指向內部結構,如果復制容器,則應該非常小心地使用迭代器。例句:
QVector<int> a, b;a.resize(100000); // make a big vector filled with 0.QVector<int>::iterator i = a.begin();// WRONG way of using the iterator i:b = a;/*Now we should be careful with iterator i since it will point to shared dataIf we do *i = 4 then we would change the shared instance (both vectors)The behavior differs from STL containers. Avoid doing such things in Qt.*/a[0] = 5;/*Container a is now detached from the shared data,and even though i was an iterator from the container a, it now works as an iterator in b.Here the situation is that (*i) == 0.*/b.clear(); // Now the iterator i is completely invalid.int j = *i; // Undefined behavior!/*The data from b (which i pointed to) is gone.This would be well-defined with STL containers (and (*i) == 5),but with QVector this is likely to crash.*/
上面的例子只顯示了QVector的問題,但這個問題存在于所有隱式共享的Qt容器中。
結論
努力奔跑吧,反正沒有一片屬于你的風景
。