qt 容器 如qlist可不可以作为全局变量或者是静态变量?

Qt库提供了一组基于模板的一般化嘚容器类这些容器可以存储指定的类型的元素。例如如果你需要一个可变大小的Qstring数组,可以用<>.

这些容器比STL容器更轻更安全更容易使鼡。如果你不熟悉STL或者更喜欢以Qt的方式做事你可以用这些类取代STL类。

这些类是隐式共享的它们都是可重入,它们进行了速度优化用哽少的内存和最小的内联代码扩展,生成更小的可执行文件此外,当所有的线程仅仅以只读的方式访问它们时它们是线程安全的。

iterators 效率稍微高一些而且可以喝Qt的或STL的通用算法一起使用。

Qt也提供关键字 使得很容易遍历容易内的元素

Qt提供了以下顺序容器:, , , ,和 . 。对于大多數程序而言 是最好用的,即使它以数组列表实现它提供了非常快的在前面和后面追加的函数。如果你真的需要一个连接表可以用;。洳果你想让元素占用连续的内存空间可以用 .  and ,它们提供了后进先出和先进先出

这些容器被定义在不同的头文件。为了方便这些容器茬文件<QtContainerFwd>.进行了前置声明。

容器保存的类型可以是任意可赋值的数据类型因此要用容器存储,一个类型必须提供默认构造函数拷贝构造函数和赋值操作。这包含了大部分可能用容器存储的数据类型包括基本类型如int和double,指针和Qt数据类型如QstringQdate和Qtime。但是不包含QObject以及任意QObject的子类如果你视图实例化<>,编译器将抱怨QWidget的拷贝构造函数和赋值操作是不可用的如果你想用容器存储这些对象,可以以指针的方式进行保存如< *>.。

以下是满足可赋值的自定义数据类型的例子:

如果我们不提供拷贝构造函数或赋值操作C++将提供默认的实现成员拷贝。在以上的例孓这么写就足够了。如果你没有提供任何构造函数C++将提供默认构造函数,用成员的默认构造函数进行成员初始化即使没有提供任何顯示的构造函数或赋值操作,下面的数据类型也可以用容器存储

一些容器对于要进行存储的数据类型有额外的要求,例如<Key,T>的key类型必须提供operator<()。这样的特殊要求在类的详细描述中都有介绍在一些情况下,特殊的函数有特殊的要求这些只是每个函数的基础描述,当要求不滿足时编译器将报错

某些容器类的函数的文档说明设计默认构造值,例如 用默认构造值自动的初始化其元素。如果没有指定key值则() 返囙默认构造值。对于大多数值类型这意味着简单的用默认构造函数创建默认值。但是对于原始类型如intdouble和指针,C++语言不指定任何初始化在这种情况下,Qt容器自动的初始化为0.

迭代器提供了访问容器元素的统一方法Qt容器提供两种迭代器:Java-style iterators and STL-style iterators.。当容器中的数据被修改或者由于調用非const成员函数生成隐式共享数据副本时这两种迭代器都会无效。

对于美国容器类有两种Java-style iterator数据类型,一种是只读一种可读写:

与STL-style iterators 不哃的是, Java-style iterators指向元素的中间而不是直接指向元素的。因此它们指向容器的开头,容器的末尾或者两个元素的之间如下图所示:


以下是典型嘚遍历 <>并输出到控制台的循环:

它的工作原理如下: 的迭代传递到  的构造函数。此时迭代器指向list的第一个元素之前。然后调用  检测迭代器后面是否有元素如果有,我们调用 跳过该元素 返回所跳过的元素。对于 <>,其元素的类型是 .

这段代码与正向遍历的对称,除了开始调鼡 把迭代器移到最后元素的后面


不提供插入或删除元素的函数。要实现插入删除可以用,下面例子从 <int> 中删除奇书:

循环中每次调用next()都跳过后面的元素 函数从list中删除最近跳过的元素。调用 不会是迭代器失效所以可以安全的迹象使用它。反向遍历也一样:

如果想改变存茬的一个元素可以用.

和 一样 作用于最近跳过的元素。如果是正向遍历这个元素在迭代器的前面,如果是反向遍历该元素在迭代器的后面。

  返回一个元素的非const引用对于简单的操作,我们不需要::

如以上提到的,, ', 和 的迭代器的API 跟 的一样。现在来讨论 它遍历(key,value)对多少有些不同。

对于每个容器都有两种STL-style iterator类型:一种是只读,一种是可读写的应该进可能的使用只读的迭代器,因为它比可读寫的迭代器快

iterators 直接指向元素,begin()返回指向容器的第一个元素的迭代器end() 返回一个迭代器指向超过容器最后一个元素的虚拟的元素。end() 标记无效的位置不能解引用,典型的用于循环终止条件如果list是空的,begin() 和 end(),相等循环不会被执行。

下图的红色箭头显示的是有效的迭代器位置:


到目前为止的代码中我们用* 操作获取元素的值,然后调用了() 大部分的编译器也支持 i->toLower(),,有的则不支持

++和—操作都可以用做前缀和后綴。前缀修改迭代器并返回修改后的迭代器后缀版本先复制迭代器,再修改返回复制的副本。当忽略返回值的表达式中我们建议使鼡前缀版本,因为它比较快一些

对于非const迭代器类型,*操作的返回值可以用作复制操作的左值

由于有隐式共享,函数返回容器代价不是佷高Qt的API包含了很多返回 或者  的函数。如果你想用 STL 迭代器遍历它们应该先拷贝容器并遍历拷贝的容器副本:

对于返回容器的const或者非const引用嘚函数并不会出现这种情况。

foreach 代码比使用迭代器的同等代码要短:

如果数据类型不包含逗号用于遍历的变量可以声明在foreach 之内:

和其他C++循環结构一样,你可以在foreach 循环内用分支可以可以用break跳出循环:

当进入foreach 循环时Qt自动拷贝容器。如果你在遍历的时候改变了容器也不会影响循环。(即使你不改变容器也一样会进行拷贝容器,多亏了隐式共享拷贝容器速度很快)

由于foreach 创建了容器的副本,所以用非const变量并不能改变原始的容器仅仅影响容器副本,这可能不是你想要的

另外,Qt也提供了伪关键字forever 无限循环:

如果你担心命名空间受污染可以在 .pro攵件中添加下面的代码行禁止这些宏:

Qt包含了三个在某些方面类似容器的模板类,这些类不提供迭代器而且不能用foreach 关键字:

算法复杂度關心当容器的元素增加时每个函数的运行有多快。例如在中间插入一个元素速度是很快的,不管里面存储了多少个元素但是,如果在 Φ插入一个元素如果 包含有很多元素时代价可能会很大,因为一般的元素都需要移动一个位置

为了描述算法复杂度,我们用以下基于big Oh嘚术语:

下表总结了Qt顺序容器类的算法复杂度:

下表总结了Qt关联容器的算法复杂度:

<T>, ,和  的元素存储在连续的内存中;<T>维护着一个指向其元素的指针数组以便快速索引元素(除非T是指针类型或者一个基本类型的指针的大小此时值本身存在数组)。<Key,T> 拥有一个hash表hash表的大小与其Φ的元素的个数成比例,要避免每次在容器后面添加元素时都进行内存重新分配这里类分配了多于实际需要的内存。

考虑下面的代码峩们从一个QString生成另一个QString:

上面的值看起来可能有点奇怪,下面是指导原则:

·         4080开始它每次增加2048个字符。这很容易理解因为现代的操作系统当再分配内存是不拷贝整个数据物理内存页仅仅是被重新整理了,而且只有第一个和最后一个也需要拷贝

<T> 对于可以用memcpy() 拷贝数据的吔用那些算法,但是对于通过调用拷贝构造函数和析构函数拷贝的数据类型则用不同的算法由于在那种情况下再分配内存的代价很高,當用完内存的时候<T>通过成倍增长以减少分配次数

显示的预先分配size个元素的内存

Qt提供了丰富的容器类型如:QList、QVector、QMap等等。详细的使用方法可以参考官方文档网上也有很多示例文章,不过大部分文章的举例都是使用基础类型:如int、QString等如果我们要存儲一个对象类型,应该如何做呢—— 当然是和int类型一样操作,因为这些容器类都是泛型的不过,我们今天要讨论的不是容器类的使用鼡法而是容器存储的对象内存如何释放的问题。

(这里提到了对象类型是指 Class/Struct可以继承自QObject,也可以是普通的C++类)

下面以QList<T>为例,直接通過代码来看一下以下几种情况的内存释放

0.测试前的一点点准备

1.在栈上创建对象,然后添加容器

对象加入到容器时会发生拷贝容器析构時,容器内的对象也会析构

2. 在堆上创建对象,然后添加到容器 

对象不会发生拷贝但容器析构后容器内的对象并未析构

3. 使用Qt智能指针来管理堆上的对象,然后添加到容器

 对象不会发生拷贝容器析构的时候,容器内对象并未析构但超过作用域后,智能指针管理的对象会析构

4.给测试对象一个parent(父对象),然后进行上述测试

这里的root对象起到了类似智能指针的作用这也是Qt的一个特性,即在父对象析构的时候会将其左右子对象析构。(注意:普通C++对象并无此特性))

5.将QList作为测试对象的parent然后进行上述测试

Qt为了防止开发者出错,将QObject的类拷贝構造函数和赋值操作符都DISABLE了这样做的好处是,一旦开发者不小心定义了一个QList<QObject>的容器在添加对象时就会得到一个编译错误,从而避免发苼隐式拷贝

总结,使用容器类存储对象时最好使用对象指针类型,如:QList<TestObj*>而不要使用 QList<TestObj> 这样的定义。建议采用 智能指针QSharedPointer 或 为对象设置parent 的方法来管理内存避免内存泄露。

我要回帖

 

随机推荐