参考书籍:STL源码剖析(候捷)

STL/iterator

 

%1.STL的六大组件:

@1.containers:各种数据结构。

@2.algorithm: 各种算法。

@3.iterator:  containers和algorithm之间的桥梁。

@4.functor:   行为类似函数,可作为算法的policy。

@5.adapters:  containers,algorithm和iterator的封装,

    例如,stack和queue是对底层deque的封装。

@6.allocator: 负责空间(不一定是内存)的分配和管理。

 

%2.设计模式--迭代器UML

 抛砖引玉篇--Iterator(STL)&template&nested type

 

 

%3.itrator:

@1.面临的问题

   各种数据在不同的容器中有不同的存储结构,

   算法用于处理各种数据,关系的是数据本身,

   如何做到数据的存储方式对算法来说是透明的??

   

@2.解决方案:迭代器模式

   集合(容器)本身给客户(算法)提供一种遍历集合(容器)里所有元   素的方法,

   同时避免暴露集合(容器)本身内部的实现。

   

@3.实现方案

   设计适当的相关类型(associated types),是迭代器的责任。

   设计适当的迭代器,则是容器的责任。

   迭代器是algorithm和containers之间的桥梁。

   

   至于算法,完全可以独立于容器和迭代器之外自行发展,只要设计时以迭代器为对外接口就行。

   

@4.迭代器最重要的编程工作就是

operator*(dereference)

    operator->(member access) 进行重载(overloading)。

%4.实现方案的具体问题及解决方法:

1.迭代器由谁负责提供具体实现??

不同的容器,数据存储方式不同,对数据的操作方式不同,

唯容器本身,才知道该设计出怎样的迭代器来走访自己,

    并执行迭代器该有的各种行为(前进、后退、取值、取用成员…)。

 

2.对于用户(算法)来说,如何获取传进来的iterator所指的Iterm的 相关类型??

利用template函数的型参类型自推导功能可以获得iterator所指 Iterm的类型。eg:

int main()

{

int i;

func(&i); //传进iterator

}

template <class I>

inline

void func(Iiter)

{

func_impl(iter,*iter);

}

template <class I, class T>

void func_impl(I iter, T t) //当该函数被调用时,编译器会自动对  template变量进行推导。

 //利用传进来的*iter获得其类型T,达到  目的

{

T tmp; // 这里解决了问题。T 就是迭代器所指Iterm的类型,本  例为int

// ... 这里做原本func() 应该做的全部工作

};

3.对问题2的改善。

    注意到?2中func函数是通过其子函数区获得iterator所指Iterm的 类型的,如果func函数本身要用到Iterm类型呢?

有没有主要到?2中func函数返回值是void类型,

    如果算法函数想返回iterator所指Iterm相关类型时,怎么办??

template函数的型参类型自推导功能只能推导型参类型,

但这只是该Iterm相关类型的其中一种,

但在其他情况下,例如无法推导函数返回值类型??想获得其他相关类 型怎么办??

Iterm相关类型由iterator自己定义,用户(算法)直接提取其类型。 即使用nested type(内嵌类型)编程方法,eg:

template <class T>

struct ConcreteIterator

{

typedef T value_type;  // 内嵌类型定义(nested type)

T* ptr;

ConcreteIterator(T* p=0) : ptr(p) { }

T& operator*() const { return *ptr; }

// ...

};

template <class I>

typename I::value_type  // 这一整行是func 的回返值型 ,value_type为ConcreteIterator中定义的内嵌类型

func(I ite)

{ return *ite; }

// ...

}

ConcreteIterator<int> ite(new int(8));

cout <<func(ite);

注意,func() 的回返型别必须加上关键词typename,因为T 是一个template 参

数,在它被编译器具体化(调用)之前,编译器对T一无所知,

换句话说编译器此时并不知道ConcreteIterator<T>::value_type 代表的是一个类型

或是一个member function 或是一个data member。

关键词typename 的用意在告诉编译器说这是一个类型,如此才能顺利通过编译。

4.上述nested type的实现方法,

前提是iterator是class type(concreteIterator是类对象,可以在 类内定义nested type),

但对于int* , double*等原生指针怎么处理??

 template泛化编程可以解决类抽象问题,

   当需要对类抽象中的特定类做特殊处理时,可以用template partial specialization。

   @1.针对(任何)template 参数更进一步的条件限制,所设计出来的  一个特化版本。

eg:  

template<typename T>

class C { ... }; // 这个泛化版本允许(接受)T 为任何类型

    //我们便很容易实现它有一个形式如下的partial   //specialization:

template<typename T>

class C<T*> { ...};//这个特化版本仅适用于「T 为原生指针」的情况

//T 为原生指标」便是「T  为任何型别」的一个更 //进一步的条件限制

为了简化客户编程,可以将iterator为class type和原生指针两种情 况用class iterator_triats进行封装,用户使用该class提供的同一 接口即可获得不同iterator的特性。

下面这个class template 专门用来「萃取」迭代器的特性,而value type 正是迭代器所指Iterm的类型:

情况一:class type的iterator进行trait:

template <class I>

struct iterator_traits{ // traits 意为「特性」

typedef typename I::value_type  value_type;

};

这个所谓的traits,其意义是,如果I(传进来的iterator)定义有自 己的value type,

那么透过这个traits 的作用,萃取出来的value_type 就是 I::value_type。

先前那个func() 可以改写成这样:

template <class I>

typenameiterator_traits<I>::value_type//这一行是函数返回值类型

func(I ite)

{ return *ite; }

情况二:对原生指针的iterator进行trait:

//template partial specializations

template <class T>

struct iterator_traits<T*> {  // 偏特化版— 迭代器是个原生指标

typedef T value_type;

};

注意:iterator是const T* 时,获得的value_type为const T类 型,这对客户来说,使用或返回一个无法赋值(const 常量)的临时变 量是没有意义的。

   所以应该增加一个template partial specialization版本,将const 改为non-const,如下:

   template <classT>

   struct iterator_traits<const T*> { //偏特化版— 当迭代器是个 //pointer-to-const

Typedef  T value_type;  //萃取出来的型别应该是T 而非const T

  };

小结traits:

traits 编程技法,大量运用于STL 实作品中。它利用nested type(内 嵌类型)的编程技巧与编译器的template 自变量推导功能,补强C++ 未 能提供的关于类型识别方面的能力。

?5.iterator的相关类型(associated type)究竟有哪些??

 抛砖引玉篇--Iterator(STL)&template&nested type

:

@1.迭代器相应型别之一:value type 

所谓value type,是指迭代器所指对象的类型

 

@2.迭代器相应型别之二:difference type 

difference type 用来表示两个迭代器之间的最大距离,也因此, 它可以用来表示一个容器的最大容量,因为对于连续空间的容器而 言,头尾之间的距离就是其最大容量。

如果一个泛型算法提供计数功能,例如STL 的count(),其传回值 就必须使用迭代器的difference type:

template <class I, class T>

typename iterator_traits<I>::difference_type//这一整行是  //函数回返类型

count(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;

}

 

针对相应型别difference type,traits 的两个(针对原生指标而 写的)特化版本如下,以C++ 内建的ptrdiff_t(定义于<cstddef> 表头档)做为原生指标的difference type

 

@3.迭代器相应型别之三:reference type 

从「迭代器所指Iterm的内容是否可修改」的角度看,

迭代器分为两种:

##1.不允许改变「所指对象内容」,称为constant iterators,

例如const int* pic;

##2.允许改变「所指对象内容」,称为mutable iterators,

例如int* pi。

当我们对一个mutable iterators 做dereference(*)动作时,获 得的不应该是个右值(rvalue),应该是个左值(lvalue),因为右 值不允许赋值动作(assignment)。

C++ 中,函式如果要传回左值,都是以by reference的方式进行,

%%1.当p 是个mutable iterators 时,如果其value type 是T, 那么*p 的型别不应该是T,而是T&。

%%2.当p是个constant iterators,其value type 是T,那么*p 的 型别不应该是const T,而应该是const T&。这里所讨论的*p 的 型别,即所谓的reference type

@4. 迭代器相应型别之四:pointer type 

回传一个指针p,既可以获得p所指的地址,

   也可以获得p所指地址上的内容(*p)。

 

@5. 迭代器相应型别之五:iterator_category 

根据移动特性与施行动作,迭代器被分为五类:

Input Iterator:这种迭代器所指对象,不允许外界改变,read only。

Output Iterator:只写(write only)。

Forward Iterator:可读可写,算法(例如replace())

Bidirectional Iterator:可双向移动。

Random Access Iterator:前四种迭代器都只供应部份指针算术能 力(前三种支持operator++,第四种再加上operator--),

第五种则涵盖所有指针算术能力,包括p+/-n, p[n], p1-p2, p1<p2。

 

 抛砖引玉篇--Iterator(STL)&template&nested type

相关文章:

  • 2021-04-10
  • 2022-01-13
  • 2021-05-15
  • 2021-07-17
  • 2021-06-23
  • 2021-12-31
  • 2021-08-09
  • 2021-07-06
猜你喜欢
  • 2021-12-06
  • 2021-11-27
  • 2022-01-28
  • 2021-09-29
  • 2022-01-23
  • 2022-12-23
  • 2021-11-13
相关资源
相似解决方案