QList 和 QVector
来自Qt官方文档和Effective Qt
- QList<T>、QLinkedList<T>和QVector<T>提供了类似的API和功能。它们通常是可互换的,但会对性能产生影响。
-
QVector应该是默认的第一选择。 QVector<T>通常比QList<T>提供更好的性能,因为QVector<T>总是在内存中顺序存储其项,
其中QList<T>在堆上分配其项,除非sizeof(T) <= sizeof(void*),并且T 已使用Q_DECLARE_TYPEINFO
声明为 Q_MOVABLE_TYPE 或者 Q_PRIMITIVE_TYPE using Q_DECLARE_TYPEINFO。 - QList与std::list无关,而QSet与std::set无关。
- 与QList相比,vector(std or Q)是更好的选择。
- 将结果分配给常量迭代器时,始终使用constBegin()/constEnd()(cbegin()/cend())。
- 避免在迭代时修改容器(删除/插入元素)。如果必须,请使用STL算法,例如std::remove()家族中的一个。
- 与Java风格的迭代器相比,STL兼容的迭代器更好。
- 如果确实要使用Java风格的迭代器,请避免使用mutating迭代器。
- 最好使用const引用作为Q_FOREACH的第一个参数,除非元素类型通常按值传递。
- 如果枚举和QFlags有可能保存在Qt容器中,则将它们声明为Q_PRIMITIVE_TYPE。
- 声明值类型Q_MOVABLE_TYPE,如果它们有可能被保存在Qt容器中。
- 避免QList中的T未声明为Q_MOVABLE_TYPE 或 Q_PRIMITIVE_TYPE,或者其中sizeof(T)!=sizeof(void*)(请记住同时检查32位和64位平台)。
- 注意:只要被引用的项仍在容器中,QLinkedList中的迭代器和QList中的堆引用仍然有效。对于迭代器和对QVector的引用以及非堆分配的QList,情况并非如此。
- 在QList<T>内部,如果sizeof(T)<= sizeof(void*),并且T已使用Q_DECLARE_TYPEINFO声明为Q_MOVABLE_TYPE或Q_PRIMITIVE_TYPE,则QList<T>表示为一个T数组。否则,QList<T>表示为一个T*数组,并在堆上分配项。
QList<T>中这样的数组表示允许其非常快速的插入和基于索引的访问。prepend()和append()操作也非常快,因为QList在其内部数组的两端预先分配内存。
但是,请注意,当不满足上述条件时,新项的每次追加或插入都需要在堆上分配新项,这种每项分配使QVector成为执行大量添加或插入的更好选择,因为QVector可以在单个堆中为多个项分配内存。
QList 的两种存储方式:
针对不同的元素,QList 有两种方式存储元素:
(1).当“元素的占用空间”<=“指针占用的空间”,即 sizeof(T) <= sizeof(void*),并且元素已经使用 Q_DECLARE_TYPEINFO 声明为 Q_MOVABLE_TYPE
或 Q_PRIMITIVE_TYPE 时,那么 QList 存储方式和 QVector、QVarLengthArray一样,都是以数组的形式存储,即 QList<T> 表示为 T 的数组。
(2).当“元素的占用空间”>“指针占用的空间”,那么 QList 会给每一个元素 new 到堆上,即 QList<T> 表示为 QList<T*> 的数组;
所以第一种 QList 要比第二种 QList 访问起来更快。由于 QList 在内部已经为数组的两端预留了内存空间,所以第一种 QList 执行 prepend() 和 append() 操作会非常快。
第二种 QList 执行这俩操作时,每加入一个元素就会在堆上分配一次,所以会慢点。
QList 容器在其生命周期内只会变大不会缩小,只有分配给另一个 QList 时析构函数和赋值运算符才会释放多余的空间。
- at() 比 operator 更快,因为它不会发生深拷贝。
扩展:
Q_DECLARE_TYPEINFO(Type, Flags)
可以使用此宏指定有关自定义类型的信息。有了准确的类型信息,Qt的通用容器可以选择合适的存储方法和算法。
标志可以是以下之一:
Q_PRIMITIVE_TYPE指定该类型是一个POD(纯旧数据)类型,没有构造函数或析构函数,或者是一个类型,其中每个位模式都是有效的对象,memcpy()创建该对象的有效独立副本。
Q_MOVABLE_TYPE指定该类型具有构造函数和/或析构函数,但可以使用memcpy()在内存中移动。注意:尽管有名称,但这与移动构造函数或C++移动语义无关。
Q_COMPLEX_TYPE(默认值)指定该类型具有构造函数和/或析构函数,并且不能在内存中移动它。