QVector理论总结
一、概述
QVector是Qt的泛型容器类之一。它将每一个元素存储在相邻的内存位置,并提供快速的基于索引的访问。
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在Qt api中用于传递参数和返回值。使用QList与这些api进行接口。
如果需要一个真实的链表,它保证在链表中间进行常数时间的插入,并且使用迭代器指向项目,而不是索引,可以使用QLinkedList。
- 注意:QVector和QVarLengthArray都保证了c兼容的数组布局。QList没有。如果的应用程序必须与C API接口,这可能很重要。
- 注意:只要被引用的项仍在容器中,QLinkedList中的迭代器和堆分配QList中的引用都是有效的。对于迭代器和QVector的引用以及非堆分配的QList则不是这样。
二、使用
1. 声明初始化
下面是一个 存储整数的 QVector 和 存储 QString 的QVector的例子:
QVector<int> integerVector;
QVector<QString> stringVector;
QVector将其项存储在vector(数组)中。通常,vector是用初始大小创建的。
例如,下面的代码构造了一个包含200个元素的QVector:
QVector<QString> vector(200);
元素会自动用默认构造的值初始化(如果是int、double之类的话就是 0, QString就是空字符串)。如果你想用不同的值初始化vector,将该值作为第二个参数传递给构造函数:
QVector<QString> vector(200, "Pass");
还可以在任何时候调用 fill() 来用值填充 QVector。
QVector<QString> vector(3);
vector.fill("Yes");
// vector: ["Yes", "Yes", "Yes"]
vector.fill("oh", 5);
// vector: ["oh", "oh", "oh", "oh", "oh"]
2. 获取元素和链表信息
QVector使用基于0的索引,就像c++数组一样。要访问位于特定索引位置的项,可以使用operator [ ]。对于非const向量,operator[ ] 返回一个指向可用于赋值操作左侧的项的引用:
if (vector[0] == "Liz")
vector[0] = "Elizabeth";
对于只读访问,另一种语法是使用at():
for (int i = 0; i < vector.size(); ++i) {
if (vector.at(i) == "Alfonso")
cout << "Found Alfonso at position " << i << Qt::endl;
}
at()可能比operator更快,因为它永远不会导致发生深度复制。 推荐使用at( )
访问存储在QVector中的数据的另一种方法是调用data()。该函数返回指向vector中第一项的指针。可以使用指针直接访问和修改存储在vector中的元素。如果需要将QVector传递给接受普通c++数组的函数,则该指针也很有用。
如果你想找到一个向量中某个特定值的所有出现情况,可以使用indexOf()或lastIndexOf()。 前者从给定的索引位置开始向前搜索,后者向后搜索。如果找到匹配项,两者都会返回匹配项的索引;否则,返回-1。例如:
int i = vector.indexOf("Harumi");
if (i != -1)
cout << "First occurrence of Harumi is at position " << i << Qt::endl;
也可以用 back() 和 front() 或者 last() 和 first() 获取首尾的值。
常用获取QVector 的属性
- contains():看QVector 是否包含特定值
- count():获取某个元素在 QVector 里面的个数。
- isEmpty():是不是空 向量
- startsWith():是不是以某个元素开头
3. 常用操作
QVector提供了添加、移动和删除项的基本函数:insert()、replace()、remove()、prepend()、append()。除了append()和replace()之外,对于较大的向量,这些函数可能很慢(线性时间),因为它们需要将向量中的许多项移动内存中的一个位置。如果你想要一个在中间提供快速插入/删除的容器类,请使用QList或QLinkedList代替。
与普通的c++数组不同,QVectors可以通过调用resize()随时调整大小。如果新的大小大于旧的大小,QVector可能需要重新分配整个向量。
QVector会通过预分配高达实际数据需求两倍的内存来减少重新分配的数量。
如果想预先知道QVector将包含多少项,可以调用reserve(),让QVector预先分配一定数量的内存。
还可以调用capacity()来找出QVector实际分配了多少内存。
注意,使用非const操作符和函数会导致QVector对数据进行深度复制。这是由于隐性共享。
QVector的值类型必须是可赋值的数据类型。这涵盖了常用的大多数数据类型,但编译器不允许将QWidget存储为值;相反,存储一个QWidget *。
4. 迭代Vector
与其他容器类一样,QVector提供了java风格的迭代器(QVectorIterator和QMutableVectorIterator)和stl风格的迭代器(QVector::const_iterator和QVector::iterator)。在实践中,这些很少使用,因为可以使用QVector的索引。
我也喜欢用 索引,但还是了解了解嘛。
除了QVector, Qt还提供了QVarLengthArray,这是一个非常底层的类,几乎没有为速度优化过的功能。也就是推荐不用
QVector不支持插入、添加、追加或替换对自身值的引用。 这个并不是说不能用迭代器来完成哈。
如果要在列表中间插入、修改或删除项,必须使用迭代器。QLinkedList提供了java风格的迭代器(QVectorIterator和QMutableVectorIterator)和 STL 风格的迭代器(QLinkedList::const_iterator和QLinkedList::iterator)。有关详细信息,请参阅这些类的文档。STL 的其实就是C++基础语法就不说了
看看 java 这个风格的,所谓前向遍历从 头 head -> 尾巴 tail ,后向遍历其实就是 从 尾巴 tail -> 头 head
- QVectorIterator
简单来说就是只读的迭代器,不允许修改元素,主要用的就是 hasNext() 前向遍历,hasPrevious() 反向遍历
简单的前向遍历如下:
QVectorIterator<float> list;
...
QVectorIterator<float> i(list);
while (i.hasNext())
qDebug() << i.next();
简单的后项向遍历如下:
QVectorIterator<float> i(list);
i.toBack();//把迭代器方向变成尾巴
while (i.hasPrevious())
qDebug() << i.previous();
- QMutableVectorIterator
简单来说就是可读可写的迭代器,一样支持前后迭代, hasNext() 前向遍历,hasPrevious() 反向遍历。
同时呢,用 remove() 删除节点,用 insert() 插入节点,用setValue()来修改节点值。
简单的前向遍历如下:
QLinkedList<float> list;
...
QMutableVectorIterator<float> i(list);
while (i.hasNext())
qDebug() << i.next();
简单的后项向遍历如下:
QMutableVectorIterator<float> i(list);
i.toBack();
while (i.hasPrevious())
qDebug() << i.previous();
简单的增删改
QMutableVectorIterator<int> i(list);
while (i.hasNext()) {
int val = i.next();
if (val < 0) {
i.setValue(-val);
} else if (val == 0) {
i.remove();
}
}
三、注意
-
最大大小
当前版本也就是Qt5 的QVector仅小于2 GB(2^31字节)。确切的值与体系结构相关,因为它依赖于管理数据块所需的开销,但该值不超过32字节。QVector中可存储元素的数量等于其大小除以每个元素的大小。 -
内存不足的情况
在内存分配失败的情况下,QVector将使用Q_CHECK_PTR宏,如果应用程序被编译时支持异常,该宏将抛出std::bad_alloc异常。如果禁用异常,那么内存不足是未定义的行为。
请注意,操作系统可能会对持有大量已分配内存的应用程序施加进一步的限制,特别是大型连续内存块。这些考虑因素、此类行为的配置或任何缓解措施都超出了Qt API的范围。