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

2023-12-14 12:29:09

概述

对于每个容器类,都有两种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容器中。

结论

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

文章来源:https://blog.csdn.net/MrHHHHHH/article/details/134916067
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。