一篇看懂QVector
简介 QVector是Qt的一个通用容器类。 它将其项存储在相邻的内存位置,并提供快速地、基于索引的访问(QVector可以看做是一个封装了一个数组的模板类[],它内部维护一个数组,并且提供给外部一些访问的方法)。QList/QLinkedList/QVector/QVarLengthArray选择讨论 QList/QLinkedList/QVector/QVarLengthArray提供...
简介
QVector是Qt的一个通用容器类。
它将其项存储在相邻的内存位置,并提供快速地、基于索引的访问(QVector可以看做是一个封装了一个数组的模板类[],它内部维护一个数组,并且提供给外部一些访问的方法)。
QList/QLinkedList/QVector/QVarLengthArray选择讨论
QList/QLinkedList/QVector/QVarLengthArray提供了类似的api和功能。它们通常可以互换,但在性能方面有所不同。下面有一些使用建议:
- 如果你不清楚用哪种容器,那么建议你使用QVector。QVector通常比QList有更好的性能,因为QVector总是将它的项按顺序存储在内存中,而QList在堆上分配项,除非sizeof(T)<=sizeof(void*),并且T已经使用Q_DECLARE_TYPEINFO声明为Q_MOVABLE_TYPE或Q_PRIMITIVE_TYPE。参阅QList优缺点。
- 不过QList在Qt API中普遍被用来传递参数和返回值。与这些API交互时,使用QList。
- 如果你确实需要一个真正的链表,它可以保证在链表中插入耗时为常数时间。并对项使用迭代器(iterator)而不是索引,那么你可以使用QLinkedList。
Note:QVector和QVarLengthArray都保证了兼容C语言的数组布局。但是QList没有这么做。如果你的应用程序必须与C API进行对接,那么这点可能需要注意。
Note:只要引用的项仍然在容器中,那么QLinkedList中的迭代器和QList分配在堆上的引用(heap-cllocating)将会一直存在。对QVector中的迭代器和引用和非堆分配的QList(non-heap-allocating)来说,情况相反。
QVector简单用法
下面是一个存储整数的QVector和存储QString的QVector的一个示例。
QVector<int> integerVector;
QVector<QString> stringVector;
QVector将它的项存储在vector(数组)中。通常,vector在创建时会带一个初始大小,下面代码是一个包含200个元素的QVector:
QVector<QString> vector(200);
vector中的元素会被自动初始化成一个默认构造值(default-constructed value)。如果你想要使用其他的值初始化vector,就通过第二个参数将其传递给构造函数。
QVector<QString> vector(200, "Pass");
你还可以使用fill()来用某个值填充vector。
QVector使用从0开始(0-based)的索引,就像c++数组一样。要访问特定索引位置的项,可以使用操作符[]()。在非常量vector中,操作符[]()返回一个能被用作左值的引用:
if (vector[0] == "Lix")
vector[0] = "xxx";
如果是只读访问(read-only),可以使用另一种写法at():
for (int i = 0; i < vector.size(); ++i) {
if (vector.at(i) == "Alfonso")
cout << "Found Alfonso at position " << i << endl;
}
at()比操作符执行更快,因为他不会进行深拷贝(deep copy)。
访问存储在QVector的数据还可以使用data()。该函数返回指向vector中第一项的指针,你可以使用这个指针直接访问和修改vector中的元素。如果你需要将QVector传递给一个接受C++数组的函数,这个指针会很有用。
如果你想要查找vector中某个特定的值,使用indexOf()或者lastIndexOf()。前者从指定索引位置开始向前搜索,后者向后搜索。若找到匹配项,则返回匹配项索引。否则返回-1,例如:
int i = vector.indexOf("harumi");
if (i != -1)
cout << "First occurrence of Harumi is at position"<< i <<endl;
如果只是想检查vector是否包含某个值,使用contains()。如果想知道某个值在vector中出现的次数,可以使用count()。
QVector提供了增删查改函数:insert()/replace()/remove()/prepend()/append()。对于存储量大的vector,这些函数可能会比较慢(线性时间-linear time),除了append()和replace(),因为它们需要将vector中的许多项在内存中移动一个位置(这点可以参考数组,因为是连续的顺序表,若要在中间插入或移除数据,许多元素都得移动)。如果你想要一个提供快速插入、删除的容器类,那么可以使用QList或者QLinkedList。
和普通的c++函数不同,QVector可以通过调用resize()随时调整大小。如果新的大小大于旧的大小,QVector可能需要重新分配整个vector。QVector试图通过预分配两倍于实际数据需要的内存来减少重新分配。
如果你预先知道QVector包含多少项,那么你可以调用reserve(),要求QVector预先分配一定数量的内存。你还可以调用capacity()来查明QVector实际分配了多少内存。
Note:使用非const操作符和函数会导致QVector对数据进行深拷贝,这是由于隐式共享(implicit sharing)。
QVector存储的值类型必须是可分配的数据类型。这涵盖了大多数的数据类型,但是编译器不允许你将QWidget存储为值。取而代之的是,你可以存一个QWidget *。一些函数有额外的要求,例如,indexOf()和lastIndexOf()希望值类型支持==()运算符。这些需求是在每个函数的基础上进行编写的。
和其他的容器一样,QVector提供了java风格的迭代器(QVectorIterator和QMutableVectorIterator)和stl风格的iterator(QVector::const_iterator和QVector::iterator)。不过在实际运用中,很少用到,因为你可以在QVector中使用索引。
除了QVector,Qt也提供QVarLengthArray,这是一个非常低级的类,几乎没有优化。
QVector不支持inserting、prepending、replacing它自己的值。如果这么做会引起应用程序报错而中止。
实际运用注意
Qt容器内存释放
-
QVector::clear(),在Qt5.6之前,它会释放vector中的内存。但是从5.7开始,它的容量(QVector中维护的数组的空间)会被保留。要释放所有的容量,你可以这么做:
QVector<T> v ...; QVector<T>().swap(v); Q_ASSERT(v.capacity() == 0);
或者使用squeeze()。
v.clear(); v.squeeze();
这点很重要,在QVector中添加元素,如果只是调用clear,不释放空间,那之后的使用过程中,它一直会占着容器中最大占用量时的空间。(第一次往容器中添加了100个元素,然后clear了,你第二次添加了50个元素,此时容器内数组还占着100个元素的空间,用vector.capacity()可以检验)
-
QVector存储的类型为指针的情况下,当QVector销毁时,存储的指针指向的空间并不会被释放,需要手动释放那些空间。比如可以这样使用:
for(int i = 0; i < v.size(); i++) { if (v[i] != nullptr) { delete v[i]; v[i] = nullptr; } } v.clear(); v.squeeze();
不过,Qt中提供了qDeleteAll专门用于清除容器的内存。可以往里面传入首尾迭代器或者容器来完全释放容器内存。
总结
QVector是Qt编程中常用的容器,对它有个大致的认识很重要。
更多推荐
所有评论(0)