《stl源码剖析》-- 迭代器概念和trait编程技法

迭代器思维

在STL中,容器和算法是分开设计的,彼此独立,然后再以一帖胶着剂将他们撮合在一起,迭代器就扮演了胶着剂的角色。

例如find()算法:

template <class InputIterator, class T>
InputIterator find(InputIterator first, 
                   InputIterator last,
                   const T& value)
{
    while (first != last && *first != value)
        ++first;
    return first;
}
该算法接受两个迭代器和一个搜寻目标,只要给予不同的迭代器,find()便能够对不同的容器进行查找操作。

迭代器的设计

迭代器是一种行为类似指针的对象,所以至少要重载operator*,operator->和operator++三个操作符。
我们以list迭代器为例,来看迭代器的内部定义。
list的节点定义如下:
template <typename T>
class ListItem
{
    public:
        ListItem(T value) : _value(value), _next(0) { }
        T value() const { return _value; }
        ListItem* next() const { return _next; }
        void setnext(ListItem* val) { _next = val; }
    private:
        T _value;           //节点值
        ListItem* _next;     //指向下一个节点的指针
};
list的定义如下:
//List链表
template <typename T>
class List {
    public:
        List() : _front(0), _end(0), _size(0) { }
    private:
        ListItem<T>* _end;  //指向最后一个节点
        ListItem<T>* _front;//指向第一个节点    
        long         _size; //链表节点数目
};
list迭代器的定义如下:
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;
    }

    ListIter operator++(int)
    {
        ListIter tmp = *this;
        ptr = ptr->next();
        return tmp;
    }

    bool operator==(const ListIter& i) const
    {
        return ptr == i.ptr;
    }
    
    bool operator!=(const ListIter& i) const
    {
        return ptr != i.ptr;
    }
};
如果要在list的迭代器上使用find()函数,还需要重载operator!=操作符,并以int和ListItem<int>作为它的两个参数型别(因为find()函数内以*iter != value来检查元素值是否吻合)
template <typename T>
bool operator!=(const ListItem<T>& item, T n)
{
    return item.value() != n;
}
现在就可以以ListIter为参数调用find()函数了
ListIter<ListItem<int> > begin(mylist.front()); //需要初始化begin迭代器
ListIter<ListItem<int> > end;
ListIter<ListItem<int> > iter;
iter = find(begin, end, 3);
迭代器的基本设计思路如下图所示:

Traits编程技法

首先得明白一点,为什么需要traits。traits的实现很简单,但要清楚traits的作用!
STL定义的迭代器中定义了很多类型,例如value_type(迭代器所指对象的类型),difference_type(表示两个迭代器之间的距离)。
我们在定义使用迭代器的模板函数时,可以直接使用这些类型,例如:
template <class I>
typename I::value_type
func(I ite)
{
    return *ite;
}
 
 
 
 但是有一个问题,traits就是为了解决这个问题:如果I是普通的指针(例如int*),那么该函数实例化就会出错,因为普通的指针并不是类类型,自然也就没有value_type成员。 
解决办法是加一个中间类,这个中间类统一处理迭代器和普通指针。
template <class I>
struct iterator_traits {
    typedef typename I::value_type  type;
};
//特化类型
template <class I>
struct iterator_traits<I*>
{
    typedef I value_type;
};
template <class I>
struct iterator_traits<const I*>
{
    typedef I value_type;
};
需要迭代器中的类型时,需要通过traits间接使用:
template <class I>
typename iterator_traits<I>::value_type
func(I ite)
{
    return *ite;
}
这样无论I是普通迭代器类型还是指针类型,都可以调用func()函数。
traits就是这个作用。

//榨汁机traits
template <class Iterator>
struct iterator_traits {
    typedef typename Iterator::iterator_category    iterator_category;
    typedef typename Iterator::value_type           value_type;
    typedef typename Iterator::difference_type      difference_type;
    typedef typename Iterator::pointer              pointer;
    typedef typename Iterator::reference            reference;   
};

//特化版本
template <class T>
struct iterator_traits<T*> {
    typedef random_access_iterator_tag      iterator_category;      //普通指针的迭代器类型是random_access_iterator_tag,后面会有解释!!
    typedef T                               value_type;
    typedef ptrdiff_t                       difference_type;
    typedef T*                              pointer;
    typedef T&                              reference;
};

template <class T>
struct iterator_traits<const T*> {
    typedef random_access_iterator_tag      iterator_category;      //普通指针的迭代器类型是random_access_iterator_tag
    typedef T                               value_type;
    typedef ptrdiff_t                       difference_type;
    typedef const T*                        pointer;
    typedef const T&                        reference;
};


其中前四个类型都容易理解,关键是第五个类型(iterator_category也就是迭代器类型)

迭代器类型

STL定义了五种迭代器类型如下图所示:

下面定义的五个classes,代表五种迭代器类型:
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 { };
每种类型的迭代器支持不同的操作,例如input_iterator只支持迭代器的++操作,bidirectional_iterator支持++和--操作,而random_access支持+n和-n操作。
如果要定义一个函数,把迭代器前移n步 void advance(iterator& i, Distance n)
如果使用iput_iterator迭代器实现,如下所示:
void advance(InputIterator& i, Distance n)
{
    while (n--)
        ++i;
}
如果使用bidirectional_iterator迭代器实现:
void advance(DidiectionalIterator& i, Distance n)
{
    if (n >= 0)
        while (n--)
            ++i;
    else
        while (n++)
            --i;
}
最好的实现方式是定义一个模板函数,能自动根据迭代器参数的类型,调用不同的函数,定义如下:
//重载函数模板
template <class InputIterator, class Distanace>
inline void __advance(InputIterator& i, Distanace n, input_iterator_tag)
{
    while (n--)
        ++i;
}

template <class InputIterator, class Distanace>
inline void __advance(InputIterator& i, Distanace n, bidirectional_iterator_tag)
{
    if (n >= 0) {
        while (n--)
            ++i;
    } else {
        while (n++)
            --i;
    }
}


template <class InputIterator, class Distanace>
inline void __advance(InputIterator& i, Distanace n, random_access_iterator_tag)
{
    i += n;
}


template <class InputIterator, class Distanace>
inline void advance(InputIterator& i, Distanace n)
{
    __advance(i, n, iterator_traits<InputIterator>::iterator_category()); //iterator_category是对应的迭代器类型
}
这样advance函数就会根据输入迭代器的类型,调用不同的实现函数!

为了符合规范,任何迭代器都应该提供五个内嵌相应类型,以利于traits萃取,为了防止忘记某一个类型,所以最好定义一个基类,在基类中定义这五个类型,
具体的迭代器实现类继承这个基类,如下所示:
//为避免写代码时挂一漏万,自行开发的迭代器最小继承下面这个iterator
template <class Category, class T, class Distanace = ptrdiff_t,
         class Pointer = T*, class Reference = T&>
struct iterator {
    typedef Category        iterator_category;
    typedef T               value_type;
    typedef Distanace       difference_type;
    typedef Pointer         pointer;
    typedef Reference       reference;
};
定义迭代器时,如下所示:
template <class Item>
struct ListIter :
    public iterator<forward_iterator_tag, Item>//迭代器类型是forward_iterator_tag
{ 
......
};
STL中还有个__type_traits定义,定义方式和traits一样,很容易看明白,作用则是用于函数重载。
掌握了traits编程技法,就掌握了stl源码的钥匙。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值