在Qt库中为我们提供了一系列的基于模板的容器类。这些类可以被用来存储特定类型的项。例如,如果你需要一个大小可以变得QString数组,那么可以使用QVector<QString>。
这些容器类都是隐式共享的,可重入的,并且在速度上进行了优化,内存占用少,内联代码扩展少,从而可以产生更小的可执行文件。此外,当他们被用作只读容器时,还是线程安全的。对于遍历这些容器来说,可以使用两种类型的迭代器:Java风格的迭代器和STL风格的迭代器。其中,Java风格的迭代器更容易使用,特别是对于Java工作人员来说,它提供了高层次的函数;然而,STL风格的迭代器会更高效,并且可以和Qt和STL的通用算法结合使用。另外,Qt还提供了一个foreach关键字,使遍历容器中的每一项更容易了。
Qt中的容器和STL中的类似,也分为序列式容器和关联式容器。其中,序列式容器有:QList,QLinkedList,QVector,QStack,QQueue。对大部分应用程序来说,QList都是一个很好的选择。尽管它在底层被实现为一个array-list,但它为我们提供了非常快速的添加操作,包括在头部添加和在尾部添加。如果你确实需要一个linked-list,可以使用QLinkedList;如果你想确保你的元素占用连续的内存空间,可以使用QVector。而QStack和QQueue是两个提供了LIFO和FIFO语义的方便类。
除了序列式容器,Qt中还提供了关联式容器:QMap,QMultiMap,QHash,QMultiHash,QSet。这些容器中存储的都是key-value对。其中,"Multi"容器又支持一个key可以关联多个value。"Hash"容器通过使用一个hash函数而不是二分搜索提供了更快速的查找操作。
我们将这些容器类的总结在下表中:
QList<T> | 这是最通用的一个容器类。它里面存储了给定类型T的一个列表,这个列表可以使用下标来访问。其实,在底层QList被实现为一个数组, 确保基于下标的访问非常快速。可以使用QList::append()和QList::prepend()向链表的两端添加元素,或者使用QList::insert()在链表的中间插入元素。 并且,和其他容器相比,更重要的是,QList在可执行文件中展开的代码量是非常少的,是经过高度优化的。QStringList就继承自QList<QString>。 |
QLinkedList<T> | 这个容器类类似于QList,只不过它是使用迭代器来访问,而不是下标。当从中间插入时,它的效率比QList还要高。并且,它有更好的迭代器语义。 即指向QLinkedList中某个元素的迭代器,只有该元素存在就会一直保持有效,而指向QList中某元素的迭代器,在向QList进行任意插入或删除时都会导致 该迭代器失效。 |
QVector<T> | 这个容器类会在一块相邻的内存中存储一个给定类型的值的数组。在一个vector的前端或中间插入是非常慢的,因为这会导致大量现存的元素移动以为新的 元素腾出位置。 |
QStack<T> | 这个容器类继承自QVector,提供了“先入后出”的语义。 |
QQueue<T> | 这个容器类继承自QList,提供了“先入先出”的语义。 |
QSet<T> | 这个容器类提供了不允许有重复值的集合,提供快速的查找效率。 |
QMap<Key, T> | 这个容器类提供了一个字典形式的容器,它会将Key类型的值映射到T类型的value上。通常情况下,每一个key只关联一个值。并且,QMap会按Key的顺序存储 相应的值;所以,如果不关心元素的存储顺序,QHash是一个更好的选择。 |
QMaultiMap<Key, T> | 这个容器类继承自QMap,提供了多值的字典,也就是说,该容器中的一个key可以关联多个值。 |
QHash<Key, T> | 这个容器类的API和QMap几乎一样,但它提供了更快速的查找操作。并且,该类会按任意的顺序存储值。 |
QMultiHash<Key, T> | 这个容器类继承自QHash,提供了多值hash表。 |
容器是可以嵌套使用的。例如,可以使用QMap<QString, QList<int>>这种类型,其key的类型是QString,值类型是QList<int>。
上面提到的这些容器分别被定义在各自的、名称和容器名一样的头文件中。例如,<QLinkedList>。
这些容器中存储的值可以是任何能被赋值的数据类型,即该类型必须提供一个默认的构造函数、一个拷贝构造函数、一个赋值运算符。这样的数据类型涵盖了大部分你可以存储的类型,包括基本类型入int和double,指针类型,Qt的数据类型QString,QDate,QTime,但不包括QObject或其子类(QWidget,QDialog,QTimer等等)。如果你尝试构建一个QList<QWidget>类型的变量,编译器就会提示你QWidget类的拷贝构造函数和赋值操作符是被禁用的。如果你想存储这些类的对象,可以存储它们的指针类型,例如QList<QWidget*>。
一个可以存储在容器中的可赋值数据类型,类似于下面这个自定义类型:
class Employee
{
public:
Employee() {}
Employee(const Employee &other);
Employee &operator=(const Employee &other);
private:
QString myName;
QDate myDateOfBirth;
};
如果我们不提供一个拷贝构造函数或赋值运算符,C++提供的默认实现是逐成员拷贝。在上面的例子中,这种默认构造函数也是足够的。同样,如果你没有提供构造函数,C++为我们提供的默认构造函数会使用成员变量所对应数据类型的默认值进行各个成员的初始化。所以,下面这种自定义数据类型虽然没有提供显式的构造函数或赋值运算符,它也可以被存储到容器中:
struct Movie
{
int id;
QString title;
QDate releaseDate;
};
Qt的容器还提供了operator<<() 和 operator>>() 以使它们可以方便的使用QDataStream类读取或写入数据。这也意味着存储在容器中的数据类型必须支持这两种操作。提供这种支持是很简单的。比如,下面的例子,是我们为上面声明的Movie结构体提供的 << 和 >>运算符:
QDataStream &operator<<(QDataStream &out, const Movie &movie)
{
out << (quint32)movie.id << movie.title
<< movie.releaseDate;
return out;
}
QDataStream &operator>>(QDataStream &in, Movie &movie)
{
quint32 id;
QDate date;
in >> id >> movie.title >> date;
movie.id = (int)id;
movie.releaseDate = date;
return in;
}