STL迭代器
迭代器是一种行为类似指针的对象,而指针的各种行为中最重要的便是内容提领和成员访问,因此,迭代器最重要的编程工作就是对operator* 和operator->进行,下面以find函数为例:
template <class InputIterator,typename T>
InputIterator find(InputIterator fitst,InputIterator end,T value){
while(fist!=end&&*first!=value)++first;
return first;
}
从上例可以看出算法和迭代器的合作,实现了以相同算法对不同容器的访问。
接下来,我们来为一个单向链表list设计一个迭代器。假设list的结构如下
template <typename T>
class ListItem{
private:
T _value;
ListItem *_next;
public:
ListItem(T value){
_value=value;
_next=NULL;
}
T value() cosnt {return _value;}
ListItem* next() const {return _next;}
};
template <typename T>
class List{
private:
long _size //链表长度
ListItem<T> *first;
ListItem<T> *end1;
public:
List(){
_size=0;
first=end=NULL;
}
void push(T value){
++_size;
end1=new ListItem<T>(value);
if(first==NULL){
first=end1;
}
end1=end1->next();
}
ListItem* front() const {return first;}
ListItem* end() const {return end1;}
void display() const {
for(ListItem<T> *p=first;p;p=p->next())
cout<<p->value()<<" ";
cout<<endl;
}
long size() const {return _size;}
};
下面是链表迭代器的简单实现
template <class Item>
class ListIter{
private:
Item *ptr;
public:
ListIter(Item *p=0): ptr(p){}
Item& operate*() const {return *ptr;}
Item* operate->() const {return ptr};
ListIter& operate++(){
ptr=ptr->next();
return *this;
}
ListIter operate++(int){
ListIter temp=*this;
++*this;
return temp;
}
bool operator==(const ListIter &arg) const {return arg.ptr==ptr;}
bool operator!=(const ListIter &arg) const {return arg.ptr!=ptr;}
};
代码测试
int main(){
List<int> *mylist=new List<int>;
for(int i=0;i<5;++i)mylist->push(i);
mylist->display(); //(0 1 2 3 4)
ListIter<ListItem<int> > begin(mylist->front());
ListIter<ListItem<int> > end1(mylist->end());
ListIter<ListItem<int> > iter;
iter=find(beigin,end,3)//从链表中查找3
if(iter==end)cout<<"NO FOUND"<<endl;
else cout<<"FOUND"<<endl;
return 0;
}
注意,由于find()函数内以*iter!=value来检查元素值是否吻合,有些是value原型是int,iter的型别是ListItem,两者之间无可供使用的operator!=,所以必须另写一个全局的operator!=重载函数,并以int
和ListItem作为他的两个参数:
template <typename T>
bool operator!=(const ListItem<T> &item,T value){
return item->value()!=value;
}
从上面的实现可以看出,为了完成一个针对List而设计的迭代器,我们暴露了太多List实现细节,载mian()之中为了制作end 和begin 两个迭代器,我们暴露了ListItem,在ListIter class中为了达成operator++
的目的,暴露了ListItem的next函数。换句话说,要设计出ListIter,首先必须对List的实现细节有非常丰富的了解。为了解决这一问题,STL将迭代器的实现交给了容器,每种容器都会以嵌套的方式在内部定以专属的迭代器,各种迭代器的接口相同,内部实现却不同,这也体现了泛型编程的概念。
迭代器的相应型别
1.value_type:
所谓value_type,是指迭代器所指对象的型别,任何一个打算与STL算法有完美搭配的class,都应该定义自己的value_type内嵌类别。
2.difference_type:
difference_type用来表示两个迭代器之间的距离,因此它也可以用来表示一个容器的最大容量,因为对于连续空间的容器而言,头尾之间的距离就是其最大容量。如果一个泛型算法提供计数功能,列入STL中的count(),其传回值就必须用迭代器的diffrence_type:
template <class I,class T>
typename iterator_traits<T>::difference_type
count(I first,I end,const T& value){
typename iterator_traits<T>::difference_type n=0;
for(;first!=end;++first){
if(*first==value)++n;
}
return n;
}
针对相应型别difference_type,traits有如下的两个特化版本,
template <class I>
struct iterator_traits{
typedef typename I::difference_type difference_type;
};
//指针
template <class I>
struct iterator_traits<T*>{
typedef ptrdiff_t difference_type;
};
//const指针
template <class I>
struct iterator_traits<const T*>{
typedef ptrdiff_t difference_type;
};
3.reference_type:
在c++中,如果函数要传回左值,都是以by reference 的方式进行,所以当p是个mutable iterators时,如果其value_type是T,那么*p的型别不应该是T,应该是T&。将此道理扩充,如果p是一个constrant iterators,其value_type是T,那么*p的型别不应该是const T,应该是const T&,这里所讨论的*p的型别,既所谓的reference_type,实现在下面
4.pointer_type
pointers和reference 在c++中有非常密切的联系,如果“传回一个左值,令它代表p所指之物”是可能的,那么“传回左值,令它代表p所指之物的地址”也一定可以,也就是说,我们能够传回一个pointer,指向迭代器所指之物。
这些相应型别在先前的ListIter class中出现过
Item& operator*()const{return *ptr;}
Item* operator->()const{return ptr;}
Item&便是ListIter的reference_type,Item*便是其pointer_type。
现在我们把reference_type和pointer type 这两个型别加入traits中
template <class I>
struct iterator_traits{
typedef T* pointer;
typedef T& reference;
};
//指针
template <class I>
struct iterator_traits<T*>{
typedef T* pointer;
typedef T& reference;
};
//const指针
template <class I>
struct iterator_traits<const T*>{
typedef T* pointer;
typedef T& reference;
};
5.iterator_category
最后一个迭代器的相应型别会引发较大规模的写代码工程,在那之前,先要知道迭代器的分类。
设计算法时,如果可能,我们尽量针对上图的某种迭代器提供一个明确定义,并针对更强化的某种迭代器提供另一种定义,这样才能在不同情况下提供最大效率。