STL学习笔记--3迭代器iterator与traits编程
Posted chengyu779394084
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL学习笔记--3迭代器iterator与traits编程相关的知识,希望对你有一定的参考价值。
iterator模式:提供一种方法,依次巡访某个聚合物(容器)所含的各个元素,而无需暴露该聚合物的内部表达式。
1、迭代器设计思维
STL在于将数据容器和算法分开,彼此独立,最后再以一帖粘合剂将它们撮合在一起。只要对算法给予不同的迭代器,就可以对不同容器进行相同的操作。
算法find():接受两个迭代器和一个搜寻目标。
//摘自SGI<stl_algo.h>
template <class InputIterator, class T>
InputIterator find(InputIterator first,InputIterator last,const T& value)
{
while (first != last && *first != value)
++first;
return first;
}
只要给与不同的迭代器,find()便能够对不同的容器进行查找。
#include <vector>
#include <list>
#include <deque>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
const int arraySize = 7;
int ia[arraySize] = { 0,1,2,3,4,5,6 };
//分别定义了三个容器类,直接初始化元素。
vector<int> ivect(ia, ia+arraySize);
list<int> ilist(ia, ia+arraySize);
deque<int> ideque(ia, ia+arraySize);
//vector<int>
vector<int>::iterator it1 = find(ivect.begin(), ivect.end(), 4);
if (it1 == ivect.end())
cout << "4 not found." << endl;
else
cout << "4 found. " << *it1 << endl;
//list<int>
list<int>::iterator it2 = find(ilist.begin(), ilist.end(), 6);
if (it2 == ilist.end())
cout << "6 not found." << endl;
else
cout << "6 found. " << *it2 << endl;
//deque<int>
deque<int>::iterator it3 = find(ideque.begin(), ideque.end(), 8);
if (it3 == ideque.end())
cout << "8 not found." << endl;
else
cout << "8 found. " << *it3 << endl;
return 0;
}
2、迭代器是一种智能指针smart pointer
迭代器的行为类似与指针。
迭代器中最重要的工作就是opertaor*
和opertaor->
的重载。
简单的list迭代器实现:
template <typename T>
class List
{
public:
void insert_front(T value);
void insert_end(T value);
void display(std::ostream &os = std::cout) const;
//...
private:
ListNode<T> *_end;
ListNode<T> *_front;
long _size;
};
template<typename T>
class ListItem
{
public:
T value() const{retrun _value;}
ListItem* next() const{return _next;}
private:
T _value;
ListItem* _next;//单向链表
};
template <class Item>
struct ListIter
{
Item *ptr;//保持与容器之间的联系
ListIter(Item *p = 0):ptr(p){}//默认构造函数
//拷贝构造和拷贝赋值不需要 合成的足够了
Item& operator*() const { return *ptr; }
Item* operator->() const { return ptr; }
//前置++,返回类型为左值,返回引用类型
ListIter& operator++()
{
ptr = ptr->next();
return *this;
}
//后置++,提供一个int类型的函数参数,返回类型为右值
//用前置++来实现后置版本
ListIter operator++(int)
{
ListIter tmp = *this;
++*this;
return tmp;
}
bool operator ==(const ListIter& I) const{ return ptr == I.ptr; }
bool operator !=(const ListIter& I) const{ return ptr != I.ptr; }
};
void main()
{
List<int> mylist;
//链表初始化
for(int i=0; i<5; i++)
{
mylist.insert_front(i);
mylist.insert_end(i+2);
}
mylist.display();//10(4 3 2 1 0 2 3 4 5 6)
//头迭代器
ListIter<ListNode<int>> begin(mylist.get_front());
//尾迭代器;使用默认构造函数 end=null
ListIter<ListNode<int>> end;
//使用默认构造函数;iter=null
ListIter<ListNode<int>> iter;
iter = find(begin,end,2);
if(iter == end)
cout<<findnum<<" not find"<<endl;
else
cout<<findnum<<" find"<<endl;
return 0;
}
find()函数内,以*iter!=value
来判断元素是否吻合。
本例中value为int,iter为ListItem<int>
类型,需提供两者间的operator!=
template <typename T>
bool operator!=(const ListItem<T>& item,T n)
{
return item.value()!=n;
}
3、迭代器相应型别(associated type)
假设算法中需要声明一个迭代器指向的对象的类型的变量?
decltype,typeid都做不到。
解决办法:函数模板的参数推导。函数模板,编译器自动根据实参类型自动推导。
4、Traits编程
template参数推导机制只能适用于函数参数,若为变量为返回值,则无法推导。因为返回值不在函数模板的作用于内。
解决方法2:声明内嵌类型。
template<class T>
struct MyIter
{
typedef T value_type; // 内嵌value_type类型定义
T* ptr;
MyIter(T* p=0) : ptr(p) {}
T& operator*() const { return *ptr; }
//....
};
template<class I>
//typename用于指明I::value_type是个类型
//如果没有typename的话,编译器将把value_type当成I的一个member或者member function。
typename I::value_type func(I ite)
{
return *ite;
}
int main(int argc, char **argv)
{
MyIter<int> ite(new int(8));
//调用func(),返回值类型为value_type
cout << func(ite) << endl;
return 0;
}
但并非所有的迭代器都是class type。对于原生指针,需要进行模板偏特化(Partial Specialization)。
C++模板的分两种:偏特化和全特化(所谓偏特化是指部分特化)。
1、特化(或者说全特化,specialized)不过是一个花哨的术语,意思是形参不再为形参,它们已经有了确定的值;
类模板可以部分特化;函数模板所有模板参数均需要提供默认实参。
2、偏特化(partial specialization)的意思是提供另一份template的定义,其本身仍然是一个template,或者说针对template参数更进一步的条件限制所设计出来的一个特化版本。
//“萃取”迭代器特性
//一般版本
template<class I>
struct iterator_traits{
typedef typename I::value_type value_type;
};
//偏特化<T*>
template<class T>
struct iterator_traits<T*>{
typedef T value_type;
};
//偏特化<const T*>
template<class T>
struct iterator_traits<const T*>{
typedef T value_type;
};
//func()函数现在可以改写为
template<class I>
typename iterator_traits<I>::value_type func(I iter)
{
return *iter;
}
最常用到的迭代器的五种类型:
1、value type 用来表示迭代器所指对象的型别;
2、difference type 用来表示两个迭代器之间的距离;
3、reference 为引用类型;
4、pointer 为指针类型;
5、iterator_category 表明迭代器的类型;
template<class T>
struct iterator_traits{
typedef typename I::iterator_category iterator_category;
typedef typename I::value_type value_type;
typedef typename I::difference_type difference_type;
//需对pointer设计const版本的偏特化
typedef typename I::pointer pointer;
typedef typename I::reference reference;
};
1、value type
用来表示迭代器所指对象的型别;
2、difference type
用来表示两个迭代器之间的距离;头尾之间的距离为容器的最大容量。
STL中count()返回值类型就是difference_type;
template<class T,class I>
typename iterator_traits<I>::difference_type count(I first,I last,const T& value)
{
typename iterator_traits<I>::difference_type n=0;
for(;first!=last;++first)
{
if(*first==value)
++n;
}
return n;
}
针对原生指针,C++内建ptrdiff_t类型。
//<T*>
template<class T>
struct iterator_traits<T*>{
typedef ptrdiff_t difference_type;
};
//<const T*>
template<class T>
struct iterator_traits<const T*>{
typedef ptrdiff_t difference_type;
};
3、reference type
4、pointer type
//<T*>
template<class T>
struct iterator_traits<T*>{
typedef T* pointer;
typedef T& reference;
};
//<const T*>
template<class T>
struct iterator_traits<const T*>{
typedef const T* pointer;
typedef const T& reference;
};
5、iterator_category 迭代器的类别
迭代器被分为五类:
1、Input Iterator:这种迭代器所指对象,不允许外界改变,只读(read only);
2、Output Iterator:唯写(write only);
3、Forward Iterator:允许「写入型」算法(例如 replace())在此种迭代器所形成的区间上做读写动作;
4、Bidirectional Iterator:可双向移动。某些算法需要逆向走访某个迭代器区间(例如逆向拷贝某范围内的元素),就可以使用 Bidirectional Iterators;
5、Random Access Iterator:前四种迭代器都只供应一部份指标算术能力(前3种支持 operator++ ,第4种再加上 operator--),第5种则涵盖所有指标算术能力,包括 p+n, p-n, p[n], p1-p2, p1<p2.
advance()
以advance()函数为例:函数有两个参数,迭代器p和数值n;函数内部将p前进或后退n步,针对不同的迭代器实现的方式不同。
//定义5个class,代表5种迭代器的类型
//五种迭代器tag的定义
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 {};
template <class InputIterator, class Distance>
inline void __advance(InputIterator &i,Distance n,input_iterator_tag)
{
//单向,逐一
while(n--) ++i;
}
template <class ForwardIterator, class Distance>
inline void __advance(ForwardIterator &i,Distance n,forward_iterator_tag)
{
__advance(i,n,input_iterator_tag());
}
template <class BidirectionalIterator, class Distance>
inline void __advance(BidirectionalIterator &i,Distance n,bidirectional_iterator_tag)
{
//双向,逐一
if(n>=0)
while(n--) ++i;
else
while(n++) --i;
}
template <class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator &i,Distance n,random_access_iterator_tag)
{
//双向,跳跃
i+=n;
}
template <class InputIterator, class Distance>
inline void advance(InputIterator &i,Distance n)
{
__advance(i,n,iterator_traits<InputIterator>::iterator_category());
}
//针对原生指针,迭代器类型为RandomAccessIterator
//<T*>
template<class T>
struct iterator_traits<T*>{
typedef random_access_iterator_tag iterator_category;
};
//<const T*>
template<class T>
struct iterator_traits<const T*>{
typedef random_access_iterator_tag iterator_category;
};
任何迭代器,类型总是该迭代器所隶属的类型中最强化的那一个
STL命名规则:以算法所能接受的最低阶迭代器类型为迭代器参数命名。
distance()
//InputIterator版本的实现。
//因为迭代器之间的继承原因,ForwardIterator,BidirectionalIterator也是同样调用这个版本实现。
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type __distance(InputIterator first,InputIterator last,input_iterator_tag)
{
iterator_traits<InputIterator>::difference_type n=0;
//逐一
while(first!=last)
{
++first;
++n;
}
return n;
}
//RandomAccessIterator版本的实现
template <class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type __distance(RandomAccessIterator first,RandomAccessIterator last,random_access_iterator_tag)
{
iterator_traits<RandomAccessIterator>::difference_type n=0;
//跳跃
//直接计算
return last-first;
}
//顶层封装distance()两个参数
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type distance(InputIterator first,InputIterator last)
{
typedef typename iterator_traits<InputIterator>::iterator_category() category;
return __distance(first,last,category);
}
5、iterator类
STL提供的iterator class如下。它不含任何成员,纯粹只是型别定义。其中后三个参数提供了默认值,则在定义新的迭代器时只需提供前两个参数即可。
template <class Category,
class T,
class Distance = ptrdiff_t,
class Pointer = T*,
class Reference = T&>
struct iterator {
typedef Category iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
};
//ListIter
std::iterator<std::forword_iterator_tag,Item>
6、SGI STL之__type_traits
- iterator_traits负责萃取迭代器的特性;
- __type_traits则负责萃取类型的特性。
根据定义与SGI<type_traits.h>
中的__type_traits,针对不同的类型属性,在编译时期完成对不同实现函数的派送决定。
//__type_traits<T>:T可以是任意类型
__type_traits<T>::has_trivial_default_constructor;
__type_traits<T>::has_trivial_copy_constructor;
__type_traits<T>::has_trivial_assignment_operator;
__type_traits<T>::has_trivial_destructor;
__type_traits<T>::is_POD_type;
上述5个式子,值只有两种值__true_type or __false_type。
根据其值来选择对类型进行快速的拷贝赋值或是安全版本方式。
C++针对标量型定义特化版本。每个成员的值都是__ture_type。
以上是关于STL学习笔记--3迭代器iterator与traits编程的主要内容,如果未能解决你的问题,请参考以下文章
STL标准库 & 范型编程学习笔记:迭代器的设计原则和Iterator Traits的作用与设计
STL源码剖析——iterators与trait编程#3 iterator_category
STL标准库 & 范型编程学习笔记(11):迭代器分类(category)对算法的影响