模板编程-迭代器
迭代器
迭代器源于指针而高于指针,并成为分割容器与算法的一条界河.在一个共同的迭代器界面约定之下,不同的算法与不同的容器只要其迭代器要求一致就可以相互组合.
迭代器分类
c++标准库中对迭代器进行了详细的分类,迭代器按其所能提供的操作,可以分为五种类型,分别为:
输入迭代器(input iterator) > 前向迭代器(forward iterator) > 双向迭代器(bidirectional iterator) > 跳转迭代器(random access iterator)以及输出迭代器(output iterator).
后者是前者的子集.输入迭代器提供的操作最少,只支持当前位置数据及前进一位。而跳转迭代器的功能最为丰富,不仅支持取数据,还可以前进或者后退多位。输出迭代器支持向所指位置写入数据。
1.输入迭代器与前向迭代器的区别.
前向迭代器与输入迭代器的要求极为相似,除去构造函数及比较操作外,二者都提供自加1操作前移一位以及去引用取值.而最关键的区别在于,前向迭代器能够保证两个迭代器实例a与b,如果a==b,则一定也满足++a=++b,而输入迭代器不能保证这一点.
例子:
1 #include <iostream> 2 #include <stack> 3 4 template<typename Stack> 5 class stack_iterator{ 6 public: 7 typedef typename Stack::value_type value_type; 8 typedef typename Stack::reference reference; 9 10 private: 11 Stack &s; 12 value_type *pos; 13 public: 14 stack_iterator(Stack &_s):s(_s),pos(_s.size()?_s.top():0){} 15 reference operator *() const{return *pos;} 16 stack_iterator& operator++ (){ 17 s.pop(); 18 pos=s.size()?&s.top():0; 19 return *this; 20 } 21 22 bool operator ==(stack_iterator const& rhs) const {return pos==rhs.pos;} 23 bool operator !=(stack_iterator const& rhs) const {return pos!=rhs.pos;} 24 }; 25 26 int main(){ 27 using namespace std; 28 int numbers[]={0,1,2,3,4}; 29 typedef stack<int> int_stack; 30 int_stack s; 31 32 for(int i=0;i<5;i++) s.push(numbers[i]); 33 34 stack_iterator<int_stack> a(s); 35 stack_iterator<int_stack> b(s); 36 37 cout<<((a==b)?"a"==b"?"a!=b")<<endl; 38 ++a;++b; 39 cout<<((a==b)?"++a==++b":"++a!=++b")<<endl; 40 41 coout<<*a<<endl;//危险操作,a所指数据已经出栈,空间已被释放 42 }
从中我们可以体会到对输入迭代器的自加1操作的意义.一旦某迭代器通过自加1操作前移一位后,则所有指向之前的迭代器不能保证仍然有效,所以迭代器的前移不是破坏了容器中的数据至少也是屏蔽了之前的数据.而基于前向迭代器的算法则不同,虽然前向迭代器只能前移,但是可以保证a==b可以得出++a==++b,故可以通过保存迭代器的多个副本来实现多次遍历.
以标准库中容器所提供的迭代器为例。vector、deque及array都能提供可写跳转迭代器。list能提供可写双向迭代器。forward_list能提供可写前向迭代器。set和multiset能提供只读双向迭代器。map和multimap能提供只读双向迭代器.
二:迭代器属性类模板
迭代器除了要提供必要的操作外,还应该包含足够的信息以描述其自身的属性,如所属迭代器类型、所指数据等.一般而言,相关属性都是通过在迭代器类型中嵌套定义类型来定实现的.因此,在标准中特别定义了一个模板类std::iterator_traits<T>为算法提取迭代器各种属性之用.
标准中为迭代器规定了五个属性:
- difference_type (迭代器差值类型)
- value_type(迭代器所指数据类型)
- pointer(数据指针类型)
- reference(数据引用类型)
- iterator_category(迭代器所属类型)
iterator_category标记了迭代器所属类型.标准中为区分迭代器类型,特意定义了如下的五种标签.
- struct input_iterator_tag {};
- struct output_iterator_tag {};
- struct forward_iterator_tag:public input_iterator_tag {};
- struct bidirectional_iterator_tag:public forward_iterator_tag {};
- struct random_access_iterator_tag:public bidirectional_iterator_tag {};
所谓标签,即一个空结构体,纯为标记类型所用.
利用iterator_category,作者可以根据迭代器类型采取不同的策略.
1 //适用于前向迭代器的advance函数实现,n次加1操作 2 template <typename I> 3 void advance_impl(I &i,typename std::iterator_traits<I>::difference_type n, 4 std::forward_iterator_tag){ 5 for(;n>0;n--) i++; 6 } 7 8 //适用于跳转迭代器的advance函数实现,直接用i+=n 9 template <typename I> 10 void advance_impl(I &i,typename std::iterator_traits<I>::difference_type n, 11 std::random_access_iterator_tag){ 12 i+=n; 13 } 14 15 template <typename I> 16 void advance(I &i,typename I::difference_type n){ 17 //以iterator_category()为哑参数指导编译器选择适当的重载实现 18 advance_impl(i,n,typename std::iterator_traits<I>::iterator_category()); 19 }