介绍
本文主要讨论C++中常见的几种循环遍历操作的语法:基于迭代器、基于Qt库中的foreach关键字、基于C++11新增的for循环语句。
基于迭代器的遍历
在C++容器中经常需要进行遍历操作,在C++11之前一般使用下面这种方式——基于迭代器的遍历:
QList<QString> list {"a", "b", "c", "d"}; QList<QString>::const_iterator citer = list.cbegin(); for (; citer != list.cend(); ++citer) qDebug() << (*citer);
获取容器的开始迭代器,然后输入当前迭代器指向的值,将迭代器指向下一个位置,依次下去直到迭代器到达容器尾端迭代器终止遍历操作。这种方式提供了可变迭代器和常量迭代器,前者可以在移动遍历过程中修改容器内容,后者则是只读式的遍历。迭代器作为一种接口,可以很方便的提供给C++中其他标准库调用,如排序、插入、删除、输入输出等。
但作为一般的遍历操作,其存在一些不足,首先就是代码冗长,其次就是:我只是想遍历容器每个元素,至于容器内元素的位置我并不关心,可是上面操作你必须小心的为迭代器赋值和移动,一旦失误导致迭代器超出容器的迭代器范围会导致未知错误。
foreach关键字
在 Qt 核心库中提供了一个 foreach 关键字,其实就是一个宏,通过预处理器实现,让我们可以很方便的进行一般的遍历操作:
QList<QString> list {"a", "b", "c", "d"}; foreach (QString s, list) qDebug() << s;
这比 Qt 库中提供的类java的迭代器代码还要少一些:
QListIterator<QString> iter(list); while(iter.hasNext()) qDebug() << iter.next();
在Qt程序中使用 foreach 确实非常的方便简洁,但使用时也需要注意一点:foreach 遍历的是原始容器的副本(通过隐式共享实现,所以非常快)所以通过非const的引用修改容器对原始容器不会产生效果,不过你也无法使用非const的常量引用来修改,因为foreach内部将容器标识为了 const(有兴趣可以看下面的代码)。
#define Q_FOREACH(variable, container) for (QForeachContainer<__typeof__(container)> _container_(container); !_container_.brk && _container_.i != _container_.e; __extension__ ({ ++_container_.brk; ++_container_.i; })) for (variable = *_container_.i;; __extension__ ({--_container_.brk; break;})) template <typename T> class QForeachContainer { public: inline QForeachContainer(const T& t) : c(t), brk(0), i(c.begin()), e(c.end()) { } const T c; // 将容器标识为 const 不可修改 int brk; typename T::const_iterator i, e; };
foreach 这种方式的优点就是:简单快捷,但不适合需要修改原始容器的场合。
C++11新曾的for循环语句
基于范围的for循环,继承了Qt库中foreach的优点,同时避免了其不足——可以修改原始容器,重写上面例子:
for (QString s : list) qDebug() << s;
如果变量类型较长,可以使用auto来让编译器自动推断,如下:
for (auto s : list) qDebug() << s;
通过非const变量引用修改原始容器:
for (auto &s : list) s = "x";
基于范围的for循环配合C++11中的auto关键字,可以大幅节省需要书写的代码量,同时也非常的简洁方便。
要说不足的话就是编译器的支持不足(C++11规范新增的),不过最新版的各种C++编译器都已经支持了,如果你的编译器支持的话,下次就记得用哦~
【Qt】C++中的循环遍历