【Qt开发流程】之容器类2:使用STL风格迭代器进行遍历

概述

对于每个容器类,都有两种stl风格的迭代器类型:一种提供只读访问,另一种提供读写访问。应该尽可能使用只读迭代器,因为它们比读写迭代器快。
在这里插入图片描述
STL迭代器的API以数组中的指针为模型。例如,++操作符将迭代器推进到下一项,*操作符返回迭代器指向的项。实际上,对于QVector和QStack(它们将元素存储在相邻的内存位置),迭代器类型只是T *的一个typedef,而const_iterator类型只是const T *的一个typedef。

示例

在本文档中,将集中讨论QList和QMap。QLinkedList、QVector和QSet的迭代器类型与QList的迭代器具有完全相同的接口;类似地,QHash的迭代器类型与QMap的迭代器具有相同的接口。
下面是按顺序遍历QList<QString>中的所有元素并将它们转换为小写的示例:

  QList<QString> list;
  list << "A" << "B" << "C" << "D";

  QList<QString>::iterator i;
  for (i = list.begin(); i != list.end(); ++i)
      *i = (*i).toLower();

与java风格的迭代器不同,stl风格的迭代器直接指向项。容器的begin()函数返回一个迭代器,该迭代器指向容器中的第一项。容器的end()函数返回一个迭代器,指向容器中最后一项后面一个位置的虚项。End()标记一个无效的位置;它永远不能被取消引用。它通常用于循环的中断条件。如果列表为空,begin()等于end(),因此我们永远不会执行循环。
下图用红色箭头显示了包含四个元素的vector的有效迭代器位置:
在这里插入图片描述
使用stl风格的迭代器进行向后迭代可以使用反向迭代器完成:

  QList<QString> list;
  list << "A" << "B" << "C" << "D";

  QList<QString>::reverse_iterator i;
  for (i = list.rbegin(); i != list.rend(); ++i)
      *i = i->toLower();
  }

在到目前为止的代码片段中,使用一元*操作符检索存储在某个迭代器位置的项(QString类型),然后对其调用QString::toLower()。大多数c++编译器也允许编写i->toLower(),但有些编译器不允许。
对于只读访问,可以使用const_iteratorconstBegin()constEnd()。例如:

  QList<QString>::const_iterator i;
  for (i = list.constBegin(); i != list.constEnd(); ++i)
      qDebug() << *i;

stl风格API

下表总结了stl风格迭代器的API:

方法 行为
*i 返回当前项。
++i 将迭代器推进到下一项
i += n 将迭代器向前移动n个元素
–i 将迭代器向后移动一项
i -= n 将迭代器向后移动n个元素
i - j 返回迭代器i和j之间的项数

++--操作符都可以作为前缀(++i--i)和后缀(i++i--)操作符使用。前缀版本修改迭代器并返回对修改后迭代器的引用;后缀版本在修改迭代器之前获取该迭代器的副本,并返回该副本。在忽略返回值的表达式中,我们建议您使用前缀操作符(++i--i),因为这些操作符略快一些。
对于非常量迭代器类型,一元*操作符的返回值可用于赋值操作符的左侧。
对于QMapQHash*操作符返回项的值组件。如果要检索键,请在迭代器上调用key()。为了对称,迭代器类型还提供了一个value()函数来检索值。例如,下面是将QMap中的所有项目打印到控制台:

  QMap<int, int> map;
  ...
  QMap<int, int>::const_iterator i;
  for (i = map.constBegin(); i != map.constEnd(); ++i)
      qDebug() << i.key() << ':' << i.value();

由于隐式共享,函数为每个值返回一个容器的成本非常低。Qt API包含几十个函数,每个值返回一个QList或QStringList(例如,QSplitter::sizes())。如果希望使用STL迭代器遍历这些对象,则应该始终获取容器的一个副本,并遍历该副本。例如:

  // RIGHT
  const QList<int> sizes = splitter->sizes();
  QList<int>::const_iterator i;
  for (i = sizes.begin(); i != sizes.end(); ++i)
      ...

  // WRONG
  QList<int>::const_iterator i;
  for (i = splitter->sizes().begin();
          i != splitter->sizes().end(); ++i)
      ...

对于返回const或非const对容器的引用的函数,不会出现此问题。

隐式共享迭代器问题

隐式共享对于stl风格的迭代器还有另一个后果:当迭代器在容器上处于活动状态时,应该避免复制容器。迭代器指向内部结构,如果复制容器,则应该非常小心地使用迭代器。例句:

  QVector<int> a, b;
  a.resize(100000); // make a big vector filled with 0.

  QVector<int>::iterator i = a.begin();
  // WRONG way of using the iterator i:
  b = a;
  /*
      Now we should be careful with iterator i since it will point to shared data
      If we do *i = 4 then we would change the shared instance (both vectors)
      The behavior differs from STL containers. Avoid doing such things in Qt.
  */

  a[0] = 5;
  /*
      Container a is now detached from the shared data,
      and even though i was an iterator from the container a, it now works as an iterator in b.
      Here the situation is that (*i) == 0.
  */

  b.clear(); // Now the iterator i is completely invalid.

  int j = *i; // Undefined behavior!
  /*
      The data from b (which i pointed to) is gone.
      This would be well-defined with STL containers (and (*i) == 5),
      but with QVector this is likely to crash.
  */

上面的例子只显示了QVector的问题,但这个问题存在于所有隐式共享的Qt容器中。

结论

努力奔跑吧,反正没有一片属于你的风景

相关推荐

  1. Python 详解:数据的高效利器

    2023-12-11 09:18:02       58 阅读
  2. 使用

    2023-12-11 09:18:02       28 阅读
  3. c++与反向

    2023-12-11 09:18:02       42 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-11 09:18:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-11 09:18:02       100 阅读
  3. 在Django里面运行非项目文件

    2023-12-11 09:18:02       82 阅读
  4. Python语言-面向对象

    2023-12-11 09:18:02       91 阅读

热门阅读

  1. TCP 和UDP 到底有啥区别

    2023-12-11 09:18:02       64 阅读
  2. 【数据结构】Treap

    2023-12-11 09:18:02       74 阅读
  3. https 加密协议

    2023-12-11 09:18:02       56 阅读
  4. TensorFlow 的基本概念和使用场景

    2023-12-11 09:18:02       57 阅读
  5. OkHttp: 使用入门

    2023-12-11 09:18:02       43 阅读
  6. PCIe中断总结-各个中断的区别

    2023-12-11 09:18:02       82 阅读
  7. 什么是漏洞扫描

    2023-12-11 09:18:02       55 阅读
  8. kubebuilder开发operator

    2023-12-11 09:18:02       49 阅读
  9. 说一下一次完整的HTTP请求过程包括哪些内容

    2023-12-11 09:18:02       62 阅读