STL标准库-迭代器
Posted 勿在浮沙筑高台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL标准库-迭代器相关的知识,希望对你有一定的参考价值。
技术在于交流、沟通,本文为博主原创文章转载请注明出处并保持作品的完整性
本节主要介绍STL六大部件中的Iterators迭代器.
在语言方面讲,容器是一个class template, 算法是一个仿函数, 分配器class template, 迭代器是一个class template, 适配器class template, 分配器class template
从图中我们可以看出算法是看不到容器的,容器跟算法的交互必要用迭代器做桥梁,那么迭代器是怎样让容器和算法满足各自的需求的呢?
我们先看一下都有哪些迭代器
struct input_iterator_tag{};//write-read迭代器 struct output_iterator_tag{};//read-only输出流 struct forward_iterator_tag : public input_iterator_tag{}//单向迭代器 forward_list multiset/map; struct bidirectional_iterator_tag : public input_iterator_tag{}//双向迭代器 hash list set map; struct random_access_iterator_tag : public input_iterator_tag{}//随机迭代器 array vector deque;
类图如下
下面我们验证一下各种容器的迭代器
这是测试代码
#include <iostream> #include <iterator> #include <array> #include <vector> #include <list> #include <set> #include <map> #include <forward_list> #include <deque> #include <unordered_set> #include <unordered_map> using namespace std; void _display_category(input_iterator_tag) { cout << "input_iterator_tag" << endl; } void _display_category(output_iterator_tag) { cout << "output_iterator_tag" << endl; } void _display_category(forward_iterator_tag) { cout << "forward_iterator_tag" << endl; } void _display_category(bidirectional_iterator_tag) { cout << "bidirectional_iterator_tag" << endl; } void _display_category(random_access_iterator_tag) { cout << "random_access_iterator_tag" << endl; } template<typename I> void display_ccategory(I iter) { typename iterator_traits<I>::iterator_category cagy; _display_category(cagy); } int main() { display_ccategory(array<int, 10>::iterator()); display_ccategory(vector<int>::iterator()); display_ccategory(list<int>::iterator()); display_ccategory(forward_list<int>::iterator()); display_ccategory(deque<int>::iterator()); cout << endl; display_ccategory(set<int>::iterator()); display_ccategory(multiset<int>::iterator()); display_ccategory(map<int,int>::iterator()); display_ccategory(multimap<int,int>::iterator()); cout << endl; display_ccategory(unordered_set<int>::iterator()); display_ccategory(unordered_multiset<int>::iterator()); display_ccategory(unordered_map<int,int>::iterator()); display_ccategory(unordered_multimap<int,int>::iterator()); return 0; }
迭代器是怎样让容器和算法满足各自的需求的呢
template<typename _Iterator, typename _Container> class __normal_iterator { protected: _Iterator _M_current; typedef iterator_traits<_Iterator> __traits_type; public: typedef _Iterator iterator_type; typedef typename __traits_type::iterator_category iterator_category; typedef typename __traits_type::value_type value_type; typedef typename __traits_type::difference_type difference_type; typedef typename __traits_type::reference reference; typedef typename __traits_type::pointer pointer; ... }
iterator_category,表示迭代器的分类(上面的5中类型)
value_type,表示你的value类型(vecotr<int>,此时的value_type就是int)
difference_type,表示两个迭代器指针间的距离(如begin()和end()间的距离)
pointer,表示指针(没看到使用)
reference,表示引用(没看到使用)
其实算法-容器-迭代器他们之间的交互就是用这五种变量,容器创建迭代器时,迭代器获取这五个变量,容器调用算法时,算法获取迭代器的这五个变量.
下面以distance()为例,distance()的方法是算出距离,我们看其源码
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION // There are two signatures for distance. In addition to the one // taking two iterators and returning a result, there is another // taking two iterators and a reference-to-result variable, and // returning nothing. The latter seems to be an SGI extension. // -- pedwards template<typename _InputIterator, typename _Distance> inline void __distance(_InputIterator __first, _InputIterator __last, _Distance& __n, std::input_iterator_tag)//共有三个变量,分别是__first(起始位置),__lase(终点位置),迭代器分类 { //此时的迭代器分列是input_iterator_tag,也就意味着这是一种泛华类型,input_iterator_tag他的子类都可以调用这个方法 // concept requirements //该方法主要负责内存不连续的容器,如bidirectional_iterator_tag __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>) while (__first != __last) { ++__first; ++__n; } } template<typename _RandomAccessIterator, typename _Distance> inline void __distance(_RandomAccessIterator __first, _RandomAccessIterator __last, _Distance& __n, std::random_access_iterator_tag)//该方法主要负责内存连续的容器使用 { // concept requirements __glibcxx_function_requires(_RandomAccessIteratorConcept< _RandomAccessIterator>) __n += __last - __first; } /** * This is an SGI extension. * @ingroup SGIextensions * @doctodo */ template<typename _InputIterator, typename _Distance> inline void distance(_InputIterator __first, _InputIterator __last, _Distance& __n) { // concept requirements -- taken care of in __distance __distance(__first, __last, __n, std::__iterator_category(__first));//根据函数重载调用各自函数 } _GLIBCXX_END_NAMESPACE_VERSION } // namespace
现在我们来分析一下内存连续的迭代器(random_access_iterator_tag),与内存不连续的迭代器()分别调用__distance(bidirectional_iterator_tag)这个方法时的效率
假设__first与__last的距离为1000000
那么当bidirectional_iterator_tag调用distance()时
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>) while (__first != __last) { ++__first; ++__n; }
也就意味着这个函数需要++一百万次
而内存连续的random_access_iterator_tag调用distance时呢
template<typename _RandomAccessIterator, typename _Distance> inline void __distance(_RandomAccessIterator __first, _RandomAccessIterator __last, _Distance& __n, std::random_access_iterator_tag) { // concept requirements __glibcxx_function_requires(_RandomAccessIteratorConcept< _RandomAccessIterator>) __n += __last - __first; }
只需要走一次,此时你应该体验到迭代器对效率的影响了吧
还有advance()函数,我把其源码粘在下面
template<typename _InputIterator, typename _Distance> inline void __advance(_InputIterator& __i, _Distance __n, input_iterator_tag) { // concept requirements __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>) _GLIBCXX_DEBUG_ASSERT(__n >= 0); while (__n--) ++__i; } template<typename _BidirectionalIterator, typename _Distance> inline void __advance(_BidirectionalIterator& __i, _Distance __n, bidirectional_iterator_tag) { // concept requirements __glibcxx_function_requires(_BidirectionalIteratorConcept< _BidirectionalIterator>) if (__n > 0) while (__n--) ++__i; else while (__n++) --__i; } template<typename _RandomAccessIterator, typename _Distance> inline void __advance(_RandomAccessIterator& __i, _Distance __n, random_access_iterator_tag) { // concept requirements __glibcxx_function_requires(_RandomAccessIteratorConcept< _RandomAccessIterator>) __i += __n; } /** * @brief A generalization of pointer arithmetic. * @param __i An input iterator. * @param __n The @a delta by which to change @p __i. * @return Nothing. * * This increments @p i by @p n. For bidirectional and random access * iterators, @p __n may be negative, in which case @p __i is decremented. * * For random access iterators, this uses their @c + and @c - operations * and are constant time. For other %iterator classes they are linear time. */ template<typename _InputIterator, typename _Distance> inline void advance(_InputIterator& __i, _Distance __n) { // concept requirements -- taken care of in __advance typename iterator_traits<_InputIterator>::difference_type __d = __n; std::__advance(__i, __d, std::__iterator_category(__i)); } #if __cplusplus >= 201103L template<typename _ForwardIterator> inline _ForwardIterator next(_ForwardIterator __x, typename iterator_traits<_ForwardIterator>::difference_type __n = 1) { std::advance(__x, __n); return __x; } template<typename _BidirectionalIterator> inline _BidirectionalIterator prev(_BidirectionalIterator __x, typename iterator_traits<_BidirectionalIterator>::difference_type __n = 1) { std::advance(__x, -__n); return __x; } #endif // C++11 _GLIBCXX_END_NAMESPACE_VERSION } // namespace
下面介绍一下迭代器的基本使用
双向迭代器以list为例
int main () { list<int> c = {1,2,3,4,5}; list<int>::iterator iter = c.begin(); list<int>::iterator iter1 = c.begin(); //存取实际元素 cout<< *iter <<endl; //向前步进(返回新位置) cout << *(++iter) << endl; //向前步进(返回旧位置) cout << *(iter++) << endl; //向后步进(返回新位置) cout << *(--iter) << endl; //向后步进(返回旧位置) cout << *(iter--) << endl; //迭代器赋值 iter = ++iter; cout<< *iter <<endl; //判断两个迭代器是否相等 !=判断是否不相等 cout<< (iter1 == iter) << endl; return 0; }
随机迭代器以vector为例
int main () { vector<int> c = {1,2,3,4,5}; vector<int>::iterator iter = c.begin(); vector<int>::iterator iter1 = c.begin(); //取下表为n的元素 cout<<iter[3]<<endl; //向前跳n个元素(若n为负,则向后跳) cout<<*(iter+=1)<<endl; //传回iter1和iter2之间的距离 cout<< iter1-iter<<endl; //判断iter1是否在iter之前 cout<<(iter1<iter)<<endl; //判断iter1是否不在iter之后 cout<<(iter1<=iter)<<endl; return 0; }
迭代器的辅助函数
int main () { list<int> c = {1,2,3,4,5}; list<int>::iterator iter = c.begin(); list<int>::iterator iter1 = c.begin(); //使迭代器前进给定的距离 advance(iter, 3); cout << *iter <<endl; //返回两个迭代器之间的距离 cout << distance(iter, iter1) <<endl; //使迭代器前进一步 iter = next(iter); cout << *iter << endl; //使迭代器后退一步 iter = prev(iter); cout << *iter << endl; return 0; }
以上是关于STL标准库-迭代器的主要内容,如果未能解决你的问题,请参考以下文章
我为同学解难题⑬C++程序设计第6期:STL标准库之map容器
STL标准库 & 范型编程学习笔记(11):迭代器分类(category)对算法的影响