QList官方帮助文档-详细介绍部分(个人翻译)

Qt通用容器类QList<T>,使用列表存储条目,支持基于索引的快速访问、插入及删除。
QList<T>、QLinkedList<T>和QVector<T>三者的APIs和功能几乎一样。经常相互替换,但性能侧重点有区别。以下是使用概述。
默认第一选择QVector,通常QVector<T>性能优于QList<T>。这是由于QVector<T>是在内存中按顺序地存储条目,而QList<T>是在堆上为他的条目分配内存
(当sizeof(T)<=sizeof(void*),且T被声明为Q_MOVABLE_TYPE,或Q_PRIMITIVE_TYPE(需要使用Q_DECLARE_TYPEINFO)时,除外)
参考Pros and Cons of Using QList的解释

然而,QList的使用,贯穿于整个Qt APIs,通常当做传递参数和返回值。用QList与这些APIs交互更方便。
QLinkedList使用迭代器访问,而不是索引,能保证每次插入所耗时间不变,是货真价实的链式列表。

注意:QVector和QVarLengthArray都兼容C风格数组,而Qlist不支持。对于必须使用C API的应用,了解这点,或许会很重要。
注意:只要条目还在容器中,QLinkedList上的迭代器,以及内存分配在堆上QList的引用,将一直有效。

如果sizeof(T)<=sizeof(void*),且T被申明为一个Q_MOVABLE_TYPE或Q_PRIMITIVE_TYPE(需使用Q_DECLARE_TYPEINFO),本质上,QList<T>与array<T>等同。
QList<T>可理解为一个T*的数组(Array),而条目本身被分配于堆(heap)上。
数组(array)的特点是特快的插入和基于索引的访问。由于内存数组的两端预先分配了内存,QList的prepend()与append()运行也非常快。(详见Algorithmic Complexity)

需要注意的是,非以上情况,每一次追加或插入一个新条目,都需要在堆上重新分配条目内存。QVector则是一个单独连续的堆内存上分配多个条目内存。若需做大量追加和插入,QVector更合适。

另一点需要注意的是,列表生命周期中,其内在数组只会一直变大,不收缩。内在数组可被析构函数和赋值运算重新分配(一个列表被赋值为另一个列表)。

下例分别是一个存储整数的列表(QList)和一个存储日期(QDate)的列表(QList)
QList<int> integerList;
QList<QDate> dateList;

Qt有个类QStringList,继承自QList<QString>,并增加了一些便捷的函数。例如:QStringList::join()和QStringList::filter()以及
QString::split()(用于从多个字符串创建QStringLists)


QList是用于存储多个条目的列表容器,默认构造函数只会创建一个空列表,但可用初始值列表初始化一个内含条目的列表。
QList<QString> list = { "one", "two", "three" };

QList不仅提供insert(), replace(), removeAt(), move(), and swap()这几个函数用于条目的基本增、移动、删,
还提供更便捷的函数append()、operator<<()、operator+=()、prepend()、removeFirst()和removeLast()。
如下所示,operator<<()能便捷地添加多个元素到列表中。
 list << "four" << "five";

与C++数组一致,QList使用零基索引。用operator[]()访问指定索引位置的条目。
非常量列表(non-const lists),operator[]()返回条目索引,可用于赋值运算的左边
  if (list[0] == "Bob")
      list[0] = "Robert";
      
由于QList是由一组指向大于指针类型的数据类型(或不可移动)数据类型的指针构成的数组。所以访问与赋值运算需要 时间(constant time)。
对于只读访问,建议用替代函数为at()

  for (int i = 0; i < list.size(); ++i) {
      if (list.at(i) == "Jane")
          cout << "Found Jane at position " << i << endl;
  }

at()运算速度高于operator[](), 因为他不会引起深拷贝。
从列表中提出(读取出元素后,并将它从列表中删除)一个条目,然后用它做点什么,是很常见的需求。
为满足该类需求,QList提供了takeAt(), takeFirst(),和takeLast()函数。
下例为,用循环删除列表中所有条目,一次只删一个,并用delete释放条目对象:

  QList<QWidget *> list;
  ...
  while (!list.isEmpty())
      delete list.takeFirst();
      
QList在它的内部缓存中提前预分配出了额外的空间,从而使列表两端快速地增长。所以列表的两端插入和删除条目非常快(多数情况都是constant time)。
用indexOf() 或lastIndexOf()在列表中找出所有的特定值。前者从给定索引向前搜索,后者向后搜索。若找到特定值,两者都返回对应条目索引;否则,返回-1。
例:
  int i = list.indexOf("Jane");
  if (i != -1)
      cout << "First occurrence of Jane is at position " << i << endl;

用contains()检查列表中是含有特定值。用count()获取列表中特定值的个数。用replace()替换所有特定值为另一个值。
QList的值类型,必须是可被分配的数据类型。常用的大多数数据类型都包含在内,但是编译器不会允许你将QWidget按值去存储,取而代之的是,存储QWidget *。
QList一些函数有特殊的使用条件,例如:indexof()和lastIndexOf() 需要数据类型支持运算符operator==()。
这些特殊的条件,分别记录在单条函数说明中
与其他容器类一样,QList支持Java-style迭代器(QListIterator和QMutableListIterator)和STL-style迭代器(QList::const_iterator和 QList::iterator)。
单它们极少被用到。这是因为QList支持索引访问,速度与迭代器相当。

QList不支持用自己的条目值去做做插入、前追加,后追加或替换运算。这样的错误使用,会导致应用异常中止,并得到一个错误信息。

为尽可能使QList高效,它的成员函数在使用前,并不验证输入参数的合法性。
除了isEmpty(),其他成员函数都假设列表非空。成员函数使用的索引,通常是假设它是在有效范围内的。
编译时,若定义了QT_NO_DEBUG,那么,及时QList成员函数失败,错误也不会被发现。
若没定义了QT_NO_DEBUG,失败错误会被侦查出来,并用Q_ASSERT()或Q_ASSERT_X()提示一个中肯的错误原因。
为避免列表为空时成员函数调用失败的这些错误,调用其他成员函数前,先调用isEmpty()检查下列表是否为空。
若不能确定传递的索引是否在有效范围内,检查该索引是否小于size()并且大于0即可。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章