C++进阶——STL源码之迭代器(iterators)

14 篇文章 4 订阅

STL迭代器

在 STL 编程中,容器和算法是独立设计的,即数据结构和算法是独立设计的,连接容器和算法的桥梁就是迭代器了:

迭代器是一种行为类似指针的对象,而指针的各种行为中最常见也最重要的使用是:内容提领 和成员访问,所以迭代器会对operator* 和 operator-> 进行重载工作,首先来看下list容器的迭代器实现:

 template<typename _Tp>
    struct _List_iterator
    {
      typedef _List_iterator<_Tp>                _Self;
      typedef _List_node<_Tp>                    _Node;

      typedef ptrdiff_t                          difference_type;
      typedef std::bidirectional_iterator_tag    iterator_category;
      typedef _Tp                                value_type;
      typedef _Tp*                               pointer;
      typedef _Tp&                               reference;

      _List_iterator() _GLIBCXX_NOEXCEPT
      : _M_node() { }

      explicit
      _List_iterator(__detail::_List_node_base* __x) _GLIBCXX_NOEXCEPT
      : _M_node(__x) { }

      _Self
      _M_const_cast() const _GLIBCXX_NOEXCEPT
      { return *this; }

      // Must downcast from _List_node_base to _List_node to get to _M_data.
      reference
      operator*() const _GLIBCXX_NOEXCEPT
      { return static_cast<_Node*>(_M_node)->_M_data; }

      pointer
      operator->() const _GLIBCXX_NOEXCEPT
      { return std::__addressof(static_cast<_Node*>(_M_node)->_M_data); }

      _Self&
      operator++() _GLIBCXX_NOEXCEPT
      {
	_M_node = _M_node->_M_next;
	return *this;
      }

      _Self
      operator++(int) _GLIBCXX_NOEXCEPT
      {
	_Self __tmp = *this;
	_M_node = _M_node->_M_next;
	return __tmp;
      }

      _Self&
      operator--() _GLIBCXX_NOEXCEPT
      {
	_M_node = _M_node->_M_prev;
	return *this;
      }

      _Self
      operator--(int) _GLIBCXX_NOEXCEPT
      {
	_Self __tmp = *this;
	_M_node = _M_node->_M_prev;
	return __tmp;
      }

      bool
      operator==(const _Self& __x) const _GLIBCXX_NOEXCEPT
      { return _M_node == __x._M_node; }

      bool
      operator!=(const _Self& __x) const _GLIBCXX_NOEXCEPT
      { return _M_node != __x._M_node; }

      // The only member points to the %list element.
      __detail::_List_node_base* _M_node;
    };

当使用上述定义的迭代器时,可能需要获取迭代器所指之物的型别(比如:_Tp),有如下情形:

1. function template 的参数推导

#include <iostream>
#include <list>
using namespace std;

template <class I>
inline
void func(I iter) {
	func_impl(iter, *iter);
}


template <class I, class T>
void func_impl(I iter, T t) {
	T tmp; // 通过类型推导来获得所指之物的型别
	cout << "func_impl" << endl;
}


int main() 
{
	list<int> mList;
	mList.push_back(1);
	mList.push_back(2);
	mList.push_back(3);

	func(mList.begin());

	return 0;
}

2. 使用声明的内嵌型别

函数的"template 参数推导机制"推导的只是参数,无法推导函数的返回值类型,如果返回值需要使用迭代器所指对象的型别,我们需要使用迭代器的 value type:

template <class I>
inline
typename I::value_type 
func_back(I iter) {

	return *iter;
}



int main() 
{
	list<int> mList;
	mList.push_back(1);
	mList.push_back(2);
	mList.push_back(3);

	cout << " func_back value= " << func_back(mList.begin()) << endl;;


	return 0;
}

上述的函数利用了迭代器类的内嵌型别作为返回类型,这个时候是正常执行的,但是如果 I 是一个原生的指针,是不能声明内嵌型别的,这个时候就要做偏特化处理:

template <class I>
inline
I func_back(I* iter) {
    cout << "func_back 2" << endl;
    return *iter;
}

整个测试程序:

#include <iostream>
#include <list>
using namespace std;

template <class I>
inline
void func(I iter) {
	func_impl(iter, *iter);
}


template <class I, class T>
void func_impl(I iter, T t) {
	T tmp; // 通过类型推导来获得所指之物的型别
	cout << "func_impl" << endl;
}


template <class I>
inline
typename I::value_type 
func_back(I iter) {
	cout << "func_back 1" << endl;
	return *iter;
}

template <class I>
inline
I func_back(I* iter) {
	cout << "func_back 2" << endl;
	return *iter;
}


int main() 
{
	list<int> mList;
	mList.push_back(1);
	mList.push_back(2);
	mList.push_back(3);

	func(mList.begin());

	cout << " func_back value= " << func_back(mList.begin()) << endl;;

	cout << " func_back2 value= " << func_back(&mList.front()) << endl;;

	return 0;
}

输出:

Traits 编程技法

       上面通过函数偏特化的方式,来实现内嵌型别的使用,在STL中通过利用中间层 iterator_traits 来固定func的形式,减少代码的重复,唯一要做的就是为 iterator_tartis 做偏特化使其支持 pointer 和 const pointer(如果迭代器是个pointer to const,应该设法令其value_type

为non-const,否则声明一个无法赋值的临时变量是没有意义的),而并不需要对func进行偏特化处理:

下面通过中间层traita来修改上述的测试程序:

template <class T>
struct my_iterator_traits {
	typedef typename T::value_type value_type;
};

template <class T>
struct my_iterator_traits<T*> {
	typedef T value_type;
};


template <class T>
struct my_iterator_traits<const T*> {
	typedef T value_type;
};

template <class I>
inline
typename my_iterator_traits<I>::value_type
func_use_traits(I iter) {
	cout << "func_use_traits" << endl;
	return *iter;
}


int main() 
{
	list<int> mList;
	mList.push_back(1);
	mList.push_back(2);
	mList.push_back(3);

	//func(mList.begin());

	//cout << " func_back value= " << func_back(mList.begin()) << endl;;
	//cout << " func_back2 value= " << func_back(&mList.front()) << endl;

	cout << " func_use_traits value= " << func_use_traits(mList.begin()) << endl;;
	cout << " func_use_traits2 value= " << func_use_traits(&mList.front()) << endl;

	return 0;
}

输出结果和上面是一样的,但是这里并没有对func进行偏特化处理如下:

STL iterator源码

迭代器有常见有五种类型: value_type, difference_type, reference_type, pointer_type都比较容易在 traits 和相应偏特化中提取。

  template<typename _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;
    };

对 pointer 和 const pointer的偏特化:

 /// Partial specialization for pointer types.
  template<typename _Tp>
    struct iterator_traits<_Tp*>
    {
      typedef random_access_iterator_tag iterator_category;
      typedef _Tp                         value_type;
      typedef ptrdiff_t                   difference_type;
      typedef _Tp*                        pointer;
      typedef _Tp&                        reference;
    };

  /// Partial specialization for const pointer types.
  template<typename _Tp>
    struct iterator_traits<const _Tp*>
    {
      typedef random_access_iterator_tag iterator_category;
      typedef _Tp                         value_type;
      typedef ptrdiff_t                   difference_type;
      typedef const _Tp*                  pointer;
      typedef const _Tp&                  reference;
    };

但是,iterator_category一般也有5个,这个相应型别会引发较大规模的写代码工程。

  • 单向移动只读迭代器 Input Iterator
  • 单向移动只写迭代器 Output Iterator
  • 单向移动读写迭代器 Forward Iterator
  • 双向移动读写迭代器 Bidirectional Iterator
  • 所有指针的算术能力 Random Access Iterator

例如:我们实现了 advanceII, advanceBI, advanceRAI 分别代表迭代器类型是Input Iterator,Bidirectional Iterator和Random Access Iterator的对应实现:

template<class Iterator>
void advance(Iterator& i) {
    if (is_random_access_iterator(i))
        advanceRAI(i,n);
    if (is_bidirectional_iterator(i))
        advanceBI(i,n);
    else
        advanceII(i,n);
}

但这样在执行时期才决定使用哪一个版本,会影响程序效率。最好能够在编译期就选择正确的版本;而重载这个函数机制可以达成这个目标;而对于advanceXX()都有两个函数参数,型别都未定(因为都是模板参数)。为了令其同名,形成重载函数,我们必须加上一个型别已确定的函数参数,使函数重载机制得以有效运作起来;如果traits有能力萃取出迭代器的种类,我们便可利用这个"迭代器类型"相应型别作为advancexx的第三个参数,而这个相应型别必须是一个class type,不能只是数值号码类的东西,因为编译器需依赖它来进行重载决议

STL 中定义如下tag 的类型,用于上述描述形成的重载关系:

  ///  Marking input iterators.
  struct input_iterator_tag { };

  ///  Marking output iterators.
  struct output_iterator_tag { };

  /// Forward iterators support a superset of input iterator operations.
  struct forward_iterator_tag : public input_iterator_tag { };

  /// Bidirectional iterators support a superset of forward iterator
  /// operations.
  struct bidirectional_iterator_tag : public forward_iterator_tag { };

  /// Random-access iterators support a superset of bidirectional
  /// iterator operations.
  struct random_access_iterator_tag : public bidirectional_iterator_tag { };
  //@}

以下为以 advance函数,来理解iterator_category形成的重载:

//以下为私有的重载andvance函数
// input iterator
template<class inputIterator, class distance>
inline void __advance(inputIterator&i, distance n,
                      input_iterator_tag) {
    std::cout << "input tag" << std::endl;
}
// output iterator
template<class outputIterator, class distance>
inline void __advance(outputIterator&i, distance n,
                      output_iterator_tag) {
    std::cout << "output tag" << std::endl;
}

// forward iterator
template<class ForwardIterator, class Distance>
inline void __advance(ForwardIterator &i, Distance n,
                      forward_iterator_tag) {
    std::cout << "forward tag" << std::endl;
}

// bidrectional iterator
template<class BidiectionalIterator, class Distance>
inline void __advance(BidiectionalIterator &i, Distance n,
                      bidiectional_iterator_tag) {
    std::cout << "bidrectional tag" << std::endl;

}

// RandomAccess iterator
template<class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator &i, Distance n,
                      random_access_iterator_tag) {
    std::cout << "randomaccess tag" << std::endl;

}


//对外暴露接口
template<class InputIterator, class Distance>
inline void advance(InputIterator &i, Distance n) {
    // 通过Ierator_traits询问它的iterator_category是谁
    typedef typename Iterator_traits<InputIterator>::iterator_category category;
    __advance(i, n, category()); // 各型别的重载
}


//使用
int main() {
    iterator<input_iterator_tag> input;
    iterator<output_iterator_tag> output;
    iterator<forward_iterator_tag> forward;
    iterator<bidiectional_iterator_tag> bidect;
    iterator<random_access_iterator_tag> random;
    advance(input, 10);
    advance(output, 10);
    advance(forward, 10);
    advance(bidect, 10);
    advance(random, 10);
    return 0;
}

STL提供了一个iterators class 如下,如果每个新设计的迭代器都继承自它,那就可保证符合STL的规范:

  /**
   *  @brief  Common %iterator class.
   *
   *  This class does nothing but define nested typedefs.  %Iterator classes
   *  can inherit from this class to save some work.  The typedefs are then
   *  used in specializations and overloading.
   *
   *  In particular, there are no default implementations of requirements
   *  such as @c operator++ and the like.  (How could there be?)
  */
  template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t,
           typename _Pointer = _Tp*, typename _Reference = _Tp&>
    struct iterator
    {
      /// One of the @link iterator_tags tag types@endlink.
      typedef _Category  iterator_category;
      /// The type "pointed to" by the iterator.
      typedef _Tp        value_type;
      /// Distance between iterators is represented as this type.
      typedef _Distance  difference_type;
      /// This type represents a pointer-to-value_type.
      typedef _Pointer   pointer;
      /// This type represents a reference-to-value_type.
      typedef _Reference reference;
    };
 

 

以上参考:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值