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

时间: 2024-10-13 10:04:53

STL学习笔记--3、迭代器iterator与traits编程的相关文章

STL学习笔记(迭代器相关辅助函数)

advance()可令迭代器前进 #include <iterator> void advance(InputIterator& pos,Dist n); 面对Random Access(随机存取)迭代器,该函数只是简单的调用pos+=n.而对于其他任何类型的迭代器则调用++pos(--pos)n次. distance()可以处理迭代器之间的距离 #include <iterator> Dist distance(InputIterator pos1,InputIterat

STL学习笔记(迭代器配接器)

Reverse(逆向)迭代器 Reverse迭代器是一种配接器. 重新定义递增运算和递减运算.使其行为正好倒置. 如果你使用这类迭代器,算法将以逆向次序处理元素.所有标准容器都允许使用Reverse迭代器来遍历元素.下面是个例子: 1 #include <iostream> 2 #include <list> 3 #include <algorithm> 4 using namespace std; 5 6 void print(int elem) 7 { 8 cout

STL学习笔记(迭代器类型)

迭代器类型 迭代器是一种“能够遍历某个序列内的所有元素”的对象.它可以透过与一般指针一致的接口来完成自己的工作. 不同的迭代器具有不同的”能力“(行进和存取能力) Input迭代器 Input迭代器只能一次一个向前读取元素,按此顺序一个个传回元素值. 几乎所有迭代器都具备Input迭代器的能力,而且通常更强.纯粹Input迭代器的一个典型例子就是“从标准输入装置读取数据”的迭代器. 下表列出了Input迭代器的各种操作行为 Output迭代器 Output迭代器和Input迭代器相反,其作用是将

C++学习笔记之迭代器

模板是的算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型.理解迭代器是理解STL的关键. 迭代器应该具备的特征: (1)应该能够对迭代器进行解除引用的操作,以便能够访问它引用的值.即如果P是一个迭代器,则应该对*P进行定义 (2)应该能够将一个迭代器赋给另一个迭代器.如果P和Q都是迭代器,则应对P=Q定义. (3)应该能够对迭代器进行比较,看它们是否相等.即如果P和Q都是迭代器,则应对P==Q和P!=Q进行定义. (4)应该能够使用迭代器遍历容器中的所有元素,这可以通过迭代器定义的+

蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效

蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效 热度 13728 2015-7-11 23:34 |个人分类:蛋哥的学习笔记之-基于Unity的Shader编程| 音乐, Unity, Shader, 水波, Shader, Shader, Shader, Shader 一.要干些啥: 很久很久没有写文档了,前段时间做了个个人很喜欢的,自认为比较原创的小特效,所以写个文档纪念下(本人特别喜欢音乐) 思路其实很简单,首先用顶点着色器实现一般的水波特效,然后解析音频数据(我

STL学习笔记--&gt;初识STL

“这里要用char类型”; “这里要用int类型”; “其实实现这个方法只需要把另一个方法的返回值的类型和传入参数的类型改成float类型就实现了”; “其实这个算法只需要把以前写的那个稍微改动一下就行了”; ……………… 学过面向对象语言的都知道GP这个概念,就是泛型程序设计,说的再明白点就是编写不依赖于具体数据类型的程序,C++作为一门面向对象语言,当然也有泛型这个概念,这就不得不提STL(Standard Template Library,标准模板库),是被融入C++标准程序库里面的一个高

STL iterator和traits编程技法

今天终于看完了<STL源码分析>,最近忙于两个比赛的各种文档,没时间写东西,趁着看完的劲,把欠下的补上来. <Design patterns>中对于iterator模式描述如下:提供一种方法,使之能够依序寻访某个聚合物所含的各个元素,而又无需暴露该聚合物的内部结构.在STL中,iterator扮演着连接container和algorithms的作用,下面以STL find()函数展现一下iterator在container和algorithm之间的连接作用. template &l

STL学习笔记--4、序列式容器之vector

常见的数据结构:array数组,list链表,tree树,stack栈,queue队列,hash table散列表,set集合,map映射-- 根据数据在容器中的排列分为:序列式sequence和关联式associative. 序列式容器之vector 1.vector VS array: array是静态空间,一旦配置则无法改变: vector是动态空间,随着元素的加入,内部机制会自动扩充新的空间来容纳新的元素. 实现技术:对大小的控制和重新配置时的数据移动效率. 扩充空间:配置新空间.数据移

STL学习笔记(第五章 STL组件)

STL组件 若干精心勾画的组件共同合作,构筑起STL的基础.这些组件最关键的是容器.迭代器和算法. 下图演示了STL组件之间的合作 容器(Containers) 容器类别(简称容器)用来管理一组元素.为了适应不同需求,STL提供了不同类型的容器. 总的来说,容器可分为两类: 1.序列式容器Sequence containers,此乃可序群集,其中每个元素均有固定位置(取决于插入时机和地点,和元素值无关). STL提供三个定义好的序列式容器:vector.deque和list. 2.关联式容器As