《STL源码剖析》(三)——迭代器与traits编程

5 篇文章 2 订阅

1、迭代器

STL编程中,容器和算法相互独立,容器里面是数据,算法提供对数据的操作,在算法操作的过程中,要用到迭代器,迭代器可以看作是容器和算法的桥梁。
在这里插入图片描述

2、迭代器设计模式

在设计模式中,有专门的迭代器模式。描述如下:一种能够顺序访问问题中每个元素的方法,使用该方法不能暴露内部的表达方式。
在STL编程结构中,迭代器是一种模板class,迭代器在STL中得到广泛的应用,通过迭代器,容器和算法可以有机的绑定在一起,只要对算法给予不同的迭代器,比如vectot::iterator、list::iterator、std::find(),就能对不同的容器进行查找,而不需对每个容器设计对个版本。

3、智能指针

STL是泛型编程思想的产物,是以泛型编程思想为指导产生。具体来说,STL中的迭代器将泛型算法应用于某个容器,给算法提供一个访问容器的工具,iterator扮演这个角色。
迭代其实是一种智能指针,它具有一般指针的特点,能够对齐进行*和->操作。

template<typename T>
class ListIterator {//mylist迭代器
public:
    ListIterator(T *p = 0) : m_ptr(p){} //构造函数
    T& operator*() const { return *m_ptr;}  //取值,即dereference
    T* operator->() const { return m_ptr;} //成员访问,即member access
    //...
};

但是在遍历容器的时候,不可避免的要对遍历容器内部有所了解,所以,干脆把迭代器的开发工作交给容器设计者,如此,所有实现细节得以封不被使用者看到,所以每一种STL容器都有专属的迭代器。
优点:
(1)不用担心内存泄漏(类似智能指针,析构函数释放内存)
(2)对于list,取下一个元素不是通过自增而是通过next指针来取,使用智能指针实现自增和自减,从而实现统一接口。

4、模板参数推导

在算法中,你定义中间变量或返回值的时候,需要用到变量类型。C++没有提供typeof()接口,无法直接获取。
(C++提供了typeid()操作符,这个操作符只能获得型别的名称,不能声明变量)
function template 的参数推导:

template <class I>
inline void func(I iter)
{
	func_imp(iter, *iter); //传入iter和iter所指的值,class自动推导
}
void func_imp(I iter, T t)
{
	T tmp;  //T就是迭代器所指的数据类型
	...//
}
int main()
{
	int i;
	func(&i);
}

通过多层迭代,可以导出T,但是具有很大局限性,比如,希望func()返回迭代器的value type()类型返回值,但template 参数推导机制只能推导参数,无法推导函数的返回值类型。

5、声明内嵌类别

上述迭代器所指对象的型别,称之为迭代器的 value type。
在推导机制中加入内嵌型别(typedof),为指定的对象定义一个别名,然后直接获取:

template<typename T>
{
class MyIter{
public:
	typedef T value_type;//内嵌类型声明
	MyIter(T *p = 0):m_ptr(p) {}
	T & operator*() const{return * m_ptr;}
	}
private:
	T * m_ptr;
}
//以迭代器所指对象的类型作为返回类型
//注意typename是必须的,它告诉编译器这是一个类型
template<typename MyIter>
typename Myiter::value_type Func(MyIter iter)
{
	return *iter;
}
int main(int argc, const char *argv[])
{
	Myiter<int> iter(new int(600));
	std::cout<<Func(iter)<<std::endl; //print => 666
}

上述解决方案中,不是所有的迭代器都是class type,原生指针也是一种迭代器,由于原生指针不是class type, 所以没法为他设定内嵌类别。
func是一个泛型算法,那么它绝对要接受原生指针作为迭代器,下面的代码编译将失败:

int *p = new int (5);
cout<<func(p)<<endl;

6、偏特化编程 Partinal specializiation

所谓偏特化是指如果一个class template 拥有一个以上的 template 参数,我们可以针对其中某个(或多个,但不是全部)template进行特化,例如:

tempalte<class T>
class C{} //通用版本
template<class T>
class C<T*>{ } //仅仅适用于T为原生指针的情况,泛化版本的限定版

所谓特化,是特殊情况的处理,第一个类为泛化编程,T可以是任何类型,第二个版本为泛特化版本,是第一个类的特殊情况,只针对原生指针。

6.1 原生指针应对——特性“萃取”traits

STL设计了专门的“萃取”类,它专门“萃取”迭代器的特性,而value type是迭代器的特性之一:

template<class _Tp>
struct iterator_traits<_Tp*>
{
typedef ptrdiff_t difference_type;
typedef typename _Tp::value_type value_type;
... 
}

加入萃取器前后的变化:

template<class Iterator>//萃取前
typename Iterator::value_type func(Iterator iter)
{
	return *iter;
}
通过iterator_traits作用后的版本
template<class Iterator>//萃取后
typename iterator_traits<Iterator>::value_type func(Iterator iter)
{
	return *iter;
}

通过封装,可以通过类模板的转换来支持模板的特化来支持原生指针的版本!如此,无论智能指针还是原生指针,iterator_traits::value_type都能起作用,解决了上述问题。

6.2const偏特化

通过偏特化添加中间层转换的traits模板class,能实现对原生指针和迭代器的支持,对常数指针的处理如下:

iterator_traits<const int*>::value_type  // 获得的 value_type 是 const int,而不是 int
//const变量只能初始化而不能赋值,如下例将报错:
template<typename Iterator>
typename iterator_traits<iterator>::value_type func(Iterator iter){
typename iterator_traits<iterator>::value_type tmp;
temp = * iter; //编译error
}
//偏特化:
template<typename T>
struct iterator_traits<const T *>
{
	typedef T value_type; //得到T而不是const T
}

7、traits编程技法

traits编程技法就是通过增加一层中间的模板 class ,以解决获取迭代器的型别的原生指针的问题。利用一个中间层固定了func的形式,使得重复代码大量减少。要做的是特化 iterator_traits 使其支持pointer和const pointer。
传递的T为类,询问T::value_type,否则进入特化版本,T即为value_type。
总结:核心知识点在于 模板参数推导机制和内嵌类型定义,为了能处理原生指针这种特殊的迭代器,引入了偏特化机制,traits像一台”特性萃取机“,把迭代器放进去,就能榨取迭代器的特性。

8、迭代器的型别和分类

8.1 迭代器的型别

常见的迭代器相应型别有5种:
value_type:迭代器所指对象的型别,原生指针也是一种迭代器,对于原生指针int*,int即为指针所指对象的类型,也就是所谓的type_value。
difference_type:用来表示两个迭代器之间的距离,对于原生指针,STL以C++内建的ptrdiff_t作为原生指针的 difference_type。
reference_type:是指迭代器所指对象的类型的引用,reference_type一般用在迭代器的 * 运算符重载上,如果value_type是T,那么对应的reference_type是 T&;如果value_type是const T,那么对应的reference_type就是const T&。
pointer_type:就是对应的指针类型,对于指针来说,最常用的功能是operator *和operator ->两个运算符。
iterator_category:作用是标识迭代器的移动特性和可以对迭代器执行的操作,分为5类,Input_Iterator、Output_Iterator、Forward_Iterator、Bidirectional_Iterator、Random_Access Iterator。

template<typename Category, typename T, typename Distance = ptrdiff_t, typename Pointer = T*, typenmame Referencee = T&>
struct iterator//迭代器的定义
{
	typedef Category iterator_category;
	typedef T value_type;
	typedef Distance difference_type;
	typedef Pointer pointer;
	typedef Reference reference;
}

iterator class不包含任何成员变量,只有类型的定义,因此不增加额外的负担。由于后面三个类型都有默认值,在继承的时候不需提供,这个类主要用于继承,在实现的时候必须包含这5个类别。
对应的迭代器萃取机的设计如下:

template<typename I>
struct iterator_traits
{
	typedef I::iterator_category iterator_category;
	typedef I::value_type value_type;
	typedef I::difference_type difference_type;
	typedef I::pointer pointer;
	typedef I::reference reference
};
//需要针对指针和const指针设计特化版本

8.2迭代器的分类

Input Iterator:此迭代器不允许修改所指的对象,是只读的。支持==、!=、++、、->等操作。
Output Iterator:此迭代器可以进行只写操作,支持++、
等操作。
Forward Iterator:允许算法在迭代器所形成的空间进行读写操作,只能单向移动,每次只能移动一步。
Bidirectional Iterator:可双向移动,其他功能与前者相同。
Random_Access_Iterator:可随机访问,包含Iterator的所有操作。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值