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);
3Forward 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 笔记 迭代器 iterator

STL源码剖析——iterators与trait编程#3 iterator_category

STL标准库 & 范型编程学习笔记(11):迭代器分类(category)对算法的影响

STL标准库 & 范型编程学习笔记(11):迭代器分类(category)对算法的影响

STL源码分析--iterator