迭代器之分析

        笔者最近在分析STL系列源码,所取用的源码标准为SGI-STL 2.9.0。笔者并不打算详细分析STL的内部数据结构,因为数据结构这个课程为大学所学,笔者并不打算当一次复读机。

       笔者个人认为分析STL源码最大的铁板为迭代器,因为迭代器是STL容器和算法两大部件之间的桥梁,迭代器并不能单独存在,它必须要依托特定的容器对象。依照笔者个人理解,容器主要提供可供操作的数据和对象,而算法是对这组数据和对象的加工,比如说std::sort();   std::find();这两个算法需要一组数据,或者排序操作,或者查找操作。例如 std::vector<int>  vc;  需要去查找这个容器里面是否出现了一个数据value,std::find(vc.begin(),vc.end(),value);     =======>个人觉得是算法需要向容器要一组数据,告诉算法这组数据的范围。而这个范围需要迭代器划出。

        言归正传,结合源码分析,我选取了std::list作为分析对象,重点讲解list中的迭代器是如何当作桥梁使用的。

  

 

 

/**************************************

 * list的node节点,这个节点中有一个向前指和向后指向的指针,但是为什么不是指向node???

 * 却是一个指向为void的指针

 * 

 */

template <class T>

struct __list_node {

  typedef void* void_pointer;

  void_pointer next;

  void_pointer prev;

  T data;

};

 

/**************************************

 * list中的迭代器

 * 必须关注的五个要素:

 * 

 * value_type      

 *

 * 

 */

 

/**************************************

 * list迭代器是一个模板类,它的行为有点类似于指针,它有指针的所有特性,但是它还有一些其他的作用

 * 所以   重载== ,!= ,* ++ ,--

 * 

 */

template<class T, class Ref, class Ptr>

struct __list_iterator {

  typedef __list_iterator<T, T&, T*>             iterator;                                           

  typedef __list_iterator<T, const T&, const T*> const_iterator;

  typedef __list_iterator<T, Ref, Ptr>           self;

 

  typedef bidirectional_iterator_tag iterator_category;

  typedef T value_type;

  typedef Ptr pointer;

  typedef Ref reference;

  typedef __list_node<T>* link_type;

  typedef size_t size_type;

  typedef ptrdiff_t difference_type;

 

  link_type node;

 

  __list_iterator(link_type x) : node(x) {}

  __list_iterator() {}

  __list_iterator(const iterator& x) : node(x.node) {}

 

  bool operator==(const self& x) const { return node == x.node; }

  bool operator!=(const self& x) const { return node != x.node; }

  reference operator*() const { return (*node).data; }

 

#ifndef __SGI_STL_NO_ARROW_OPERATOR

  pointer operator->() const { return &(operator*()); }

#endif /* __SGI_STL_NO_ARROW_OPERATOR */

 

  self& operator++() { 

    node = (link_type)((*node).next);

    return *this;

  }

  self operator++(int) { 

    self tmp = *this;

    ++*this;

    return tmp;

  }

  self& operator--() { 

    node = (link_type)((*node).prev);

    return *this;

  }

  self operator--(int) { 

    self tmp = *this;

    --*this;

    return tmp;

  }

};

 

由结构体__list_iterator可见,一个迭代器必须关注五个元素,iterator_category、value_type、pointer、reference、size_type、difference_type. 其中pointer和reference只是留作扩充性质,SGI_STL 2.9.0版本里面没有涉及到。

      由__list_iterator结构我们可以初步窥探出一些门径,就是迭代器必须要配合容器的属性,重载一些操作符,例如双向链表++操作,必须要获取当前节点向后指向的指针,才可以知道下一个结点的地址,而类似与vector这种数组结构的容器没有此项讲究。另外就是五个特性,笔者精力有限,重点分析一下value_type。

 

先不说话,直接看一段代码:

template<typename T>

class  MyIterator

{

typedef T     value_type;

}

如上所示,如果我们要获取这个迭代器所指向类型,MyIterator::value_type即可。

 

这个时候,我们设计一个算法,用上面的信息

template<typename T>

typename MyIterator<T>::value_type Foo(MyIterator<T> & i)

{

}

当然还可以这样写

template<typename I>

typename I::value_type Foo(I& i)

{

}

嗯嗯,似乎全部问题得到解决,你似乎可以把任何元素的类型传入进去了,但是,等等!!!事情似乎还没完,我们似乎还不能得到原生指针类型的返回对象,但是一个迭代器必须支持原生指针。

 我们似乎不可以直接通过内嵌性得到原生指针的返回值,只有加入一个中间类的方式解决这个问题。

template <typename T>
class Traits
{
      typedef typename T::value_type value_type;
};
这样,我们可以通过 Traits<myIterator>::value_type 来获得myIterator的value_type,于是我们把Foo函数改写成:
template <typename I> //这里的I可以是任意类型的迭代器
typename Traits<I>::value_type Foo(I i)
{
 ...
}

然而,即使这样,那个原生指针的问题仍然没有解决,因为Trait类一样没办法获得原生指针的相关信息。于是我们祭出C++的又一件利器--偏特化(partial specialization):
template <typename T>
class Traits<T*> //注意 这里针对原生指针进行了偏特化
{
      typedef typename T value_type;
};
通过上面这个 Traits的偏特化版本,我们陈述了这样一个事实:一个 T* 类型的指针所指向的元素的类型为 T。

 


如此一来,我们的 Foo函数就完全可以适用于原生指针了。比如:
int * p;
....
int i = Foo(p);
Traits会自动推导出 p 所指元素的类型为 int,从而Foo正确返回。

过程:内嵌型别->traite类->模板偏特化=>可萃取原生指针的value type。

 

 

 

 

 

 

 

       

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值