本文主要是介绍《STL源码剖析》迭代器以及Traits设计,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
C++的class templates和function templates可以实现容器和算法的泛型化。难点和关键是设计这两者的胶着剂角色——迭代器——提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴漏该容器的内部数据结构和内部表述方式。
迭代器是一种Samart pointer。
每一种STL容器都提供有专属的迭代器!原因:为了让实现细节封装起来而不让使用者看到,所以把迭代器的开发工作交给了具体容器的设计者。
(首先明确一点:设计适当的相应型别,是迭代器的责任;设计适当的迭代器,则是容器的责任!)
Traits编程技法(特性萃取机)——解决迭代器常用的五种相应型别:
首先,我们来分析一下为什么要引入所谓的Traits。(不断加入限制条件(特例),繁衍traits的由来)
一个容器可以装各种不同类型的数据,结合value type,也就是说指迭代器所指对象的型别是各种各样的。为了解决获取“迭代器所指对象的型别”,比如“以“迭代器所指对象的型别”为型别 来 声明一个变量”,我们首先会想到
(1)function template的参数推导机制
上图中以func为对外接口,却把实际操作全部置于func_impl中。又由于func_impl是一个function template,func_impl一旦被调用,编译器自动进行template参数推导,导出型别T。(也就说,采用上述function template的参数推导机制 我们获得了“迭代器所指对象的型别”)
(2) 声明内嵌型别
我们知道,“template参数推导机制”推而导之的只是参数类型,无法推导函数的返回值型别。所以,万一,value type必须用于函数的传回值,“template参数推导机制”就行不通了。为了解决这个问题,声明内嵌型别似乎是一个好主意。
(3)偏特化
但是,并不是所有迭代器都是class type!原生指针就不是!如果不是原生指针,就无法定义内嵌型别。、?????。所以,为了让一般化概念针对特定情况做特殊化处理,我们使用——template partial specialization——在泛化设计中提供一个特化版本,也就是将泛化版本中某些template参数赋予明确的指定,提供另外一份template定义式,而其本身仍旧是templatized。
template <typename T>
class C {......} //这个泛化版本允许接受T为任何型别
template <typename T>
class C<T* > {......} //这个特化版本仅适用于“T为原生指针”的情况。,解决了内嵌型别未能解决的问题。
(4)Traits——特性萃取机,萃取各个迭代器的特性(迭代器的相应型别)
Traits,其实就是内嵌型别方法的一种升级——Traits特性萃取机,约定:自行 以内嵌型别定义的方式 定义出相应型别。
struct iterator_traits
{
typedef typename I::iterator_category iterator_category;
typedef typename I::value_type value_type;
typedef typename I::difference_type difference_type;
typedef typename I::pointer pointer;
typedef typename I::reference reference;
}
iterator_traits必须针对传入之型别为 pointer 以及 pointer-to-const 者,设计特化版本。各相应型别的具体设计详见下面。
如果希望自己开发的容器与STL水乳交融,一定要为自定义的容器的迭代器定义这五种型别。
Traits的意义是:如果 I 定义有自己的value type,那么通过traits 的作用,萃取出来的就是I::value type。不论面对的是迭代器MyIterator,或者是原生指针int * ,或者是const Int *,我们都可以通过Traits取出正确的value type。
Traits编程技法——利用了“内嵌型别”的编程技巧与编译器的template参数推导功能,增强C++未能提供的关于型别认证方面的能力。
1、迭代器相应型别之一:value type——指迭代器所指对象的型别
在类中定义自己的value type内嵌型别。
2、迭代器相应型别之二:difference type——用来表示两个迭代器之间的距离
template < class I ,class T>
typename iterator_traits<T>::difference_type const(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;
}template <class I>
struct iterator_traits{typedef typename I::difference_type difference_type;
};template <class I>
struct iterator_traits<T*>{ //针对原生指针而设计的“偏特化”版本typedef ptrdiff_t difference_type;
};
template <class I>
struct iterator_traits<const T*>{ //针对原生的pointer-to-const 而设计的“偏特化”版本typedef ptrdiff_t difference_type;
};</span>
有了上面的设计,很显然我们就可以在需要任何迭代器I的difference type的时候,统一这么写:typename iterator_traits<T>::difference_type
3、迭代器相应型别之三:reference type(引用类型)——传回左值,代表P所指之物;
迭代器相应型别之四:pointer type(指针类型)——传回一个pointer,指向迭代器所指之物;
Item& operator*() const { return *ptr; } // reference type
Item* operator->() const { return ptr; }</span> // pointer type
Item&是reference type,Item*是pointer type。“传回一个左值,令它代表p所指之物”是可以的,“传回一个地址,令它代表所指之物的地址”也是可以的。
4、迭代器相应型别之五:iterator_category——迭代器类别
iterator_category表示迭代器的分类,共有五类。input_iterator、output_iterator、forward_iterator、bidirectional_iterator、random_access_iterator,分别是只读、只写、前向移动读写、双向移动读写、随机读写。
此处,为了消除“单纯传递调用的函数”,STL运用了C++的重载技术。先来看一下五种迭代器类型的从属关系:
//迭代器各类间的关系
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 InputIterator, class Distance>
inline void _advance(InputIterator& i, Distance n, input_iterator_tag) //第一个函数——单向,逐一前进
{ while (n--) ++i;
} 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()); //产生一个暂时对象(就像int()会产生一个int暂时对象),具体调用哪一个待决定
}
附:
泛型指针,原生指针和智能指针:
1. 泛型指针 泛型指针有多种含义。 (1) 指void*指针,可以指向任意数据类型,因此具有“泛型”含义。 (2) 指具有指针特性的泛型数据结构,包含泛型的迭代器、智能指针等。 广义的迭代器是一种不透明指针,能够实现遍历访问操作。通常所说的迭代器是指狭义的迭代器,即基于C++的STL中基于泛型的iterator_traits实现的类的实例。 总体来说,泛型指针和迭代器是两个不同的概念,其中的交集则是通常提到的迭代器类。
2. 原生指针就是普通指针,与它相对的是使用起来行为上象指针,但却不是指针。 说“原生”是指“最简朴最基本的那一种”。因为现在很多东西都抽象化理论化了,所以“以前的那种最简朴最基本的指针”只是一个抽象概念(比如iterator)的表现形式之一。 原生指针即 (类型名*p)样子的指针,类型名可以是基础类型,如int,double等,也可以是一个自己定义的Class类,相反的如果一个类重载了‘*’和‘->’的运算符,可以像指针一样用‘*’和‘->’操作,就不是原生的,如iterator等。auto_ptr(在头文件memory),这是一个用来包装原生指针的对象。
3. 智能指针是C++里面的概念:由于 C++ 语言没有自动内存回收机制,程序员每次得自己处理内存相关问题,但用智能指针便可以有效缓解这类问题。 引入智能指针可以防止出现悬垂指针的情况 一般是把指针封装到一个称之为智能指针类中,这个类中另外还封装了一个使用计数器,对指针的复制等操作将导致该计数器的值加1,对指针的delete操作则会减1,值为0时,指针为NULL。
《STL源码剖析》源代码下载:http://pan.baidu.com/s/1c0x2kP6
这篇关于《STL源码剖析》迭代器以及Traits设计的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!