QT 容器及遍历
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);
}
}