- 迭代器:依序巡防某个聚合物(容器)所含的各个元素,但是不需要暴露这个聚合物的内部表述方式
- 核心思想:将容器和算法分开,彼此独立设计
- 容器和算法的泛型化,均可以使用模板,使用迭代器连接容器和算法
- 例子
template <class InputIterator,class T>
InputIterator find(InputIterator first,InputIterator last,const T& value){while (first != last && *first != value){++first;}return first;
}
- 迭代器 是一种类似智能指针的对象,最为关键的操作是内容提领和成员访问。
- 因此最关键的操作是对operator* 和 operator->进行重载工作
- 参考 智能指针auto_ptr (参考思想)
void func(){auto_ptr<std::string>ps(new std::string("input_string"));std::cout << *ps << std::endl; //输出input_stringstd::cout << ps->size() << std::endl; //输出 5//离开之前不需要使用delete进行资源的释放 auto_ptr自动释放内存
}
- 使用new动态配置一个初始值为 input_string 的string对象,并将所得到的一个结果(原生指针) 作为auto_ptr<std::string>对象的初始数值
- auto_ptr里面放的是原生指针所指对象的型别,而不是原生指针的型别
- auto_ptr 的简单实现
template<class T>
class auto_ptr{
public:explicit auto_ptr(T* p = 0):pointee(p){}template<class U>auto_ptr(auto_ptr<U>& rhs):pointee(rhs.release()){}~auto_ptr(){delete pointee;}template <class U>auto_ptr<T>& operator=(auto_ptr<U>& rhs){if (this != rhs)reset(rhs.release());return *this;}T& operator*() const {return *pointee;}T& operator->() const {return pointee;}T* get() const {return pointee;}private:T* pointee;
};
- 迭代器会暴露太多的细节,因此将迭代器交给了每一个专属的 容器,这也就是为啥每一个容器都有自己的专属迭代器的缘故
- 如何判断迭代器所指对象的型别?使用函数模板的参数推导机制
- func()为对外的接口,实际操作是由func_impl()实现的
- func_impl是一个函数模板 一旦被调用 编译器会自动进行模板的参数推导,推导出型别T
- 迭代器所指对象的型别,称为该迭代器的value type,虽然上面的参数型别的推导机制可以适用于value type,但是并非是通用的方法,如果value type必须用于函数的返回值,就无法使用template的参数推导机制,毕竟 这个只适用于参数的推导 ,不可以推导 函数的返回值
- 解决上面问题的办法 声明内嵌型别
- func()返回的型别必须加上关键词typename,因为T是一个template的参数,在编译器对其具现化之前,编译器不知道T是什么东西,也就是编译器无法知道MyIter<T>::value_type是什么类型,其代表的是一个型别?还是一个成员函数?还是数据成员?
- 关键词typename相当于告诉编译器 这是一个型别,才可以顺利通过编译
- 但是并不是所有的迭代器都是class type,原生指针就不是,如果不是class type是不可以为其定义内嵌型别的。但是STL容器必须接受原始指针作为一种迭代器,上述的办法还是不足以解决这个问题,那么如何实现对于上述一般化概念针对特殊情况(例如 针对原生指针)进行特殊化处理呢?
- 使用template partial specialization就可以实现 (偏特化)
- 偏特化的含义:如果class template拥有一个以上的template参数,可以针对其中某个(或者数个,但不是全部)的template参数进行特殊化处理,即可以在泛化版本中提供一个特殊化的版本,也就是将泛化版本中的某些template参数赋予明确的指定
template <typename U,typename V,typename T>
class C{};
- 偏特化很容易让我们陷入一种误区,比如偏特化一定是对template参数U或者V或者T或者某种组合指定某个参数值,这是不对的
- 偏特化是提供另外一份template 定义式,但是本身仍然是templatized;针对任何template参数更进一步的条件限制所设计出来的一种特殊化版本
- 例:这个泛型版本允许接受T为任何型别
template <typename T>
class C{};
- 很容易接受他有如下形式的 偏特化设计
- 这个特殊化版本 仅仅适用于“T为原始指针”的情形,T为原生指针是相较于 T为任何型别的更进一步的限制
template <typename T>
class C<T*>{ };
- 解决内嵌型别未解决的问题,也就是 原生指针不是class,因此无法为其定义内嵌型别,因此可以针对 “迭代器之template参数为指针”设计特化版的迭代器
- 这个class template专门用于 萃取 迭代器的特性,value_type正是迭代器特性之一
template <class I>
struct iteraor_traits{ //traits的含义是 "特性"typedef typename I::value_type value_type;
};
- 这个traits的含义是 如果 I 定义有自己的value type通过这个traits就可以萃取得到的value_type就是I:: value_type,如果I定义有自己的value type 先前的func()可以进一步的改下成如下形式
template <class I>
typename iteraor_traits<I>::value_type //这一整行是函数的返回型别func(I ite){return *ite;}
- 这新增的一层间接性,可以带来的好处是什么呢?可以为traits 进行偏特化的设计
- 令iterator_traits 拥有的一个偏特化的设计如下
template <class T>
struct iteraor_traits<T*>{ //偏特化版本 迭代器是一个原生指针typedef T value_type;};
- 于是 原生指针int* 虽然不是一种class type 但是仍然可以通过traits取到value type
- 注意:针对“指向常数对象的指针(pointer to const)” iterator_traits<const int *>::value_type
- 上述得到的结果是const int,而不是int,原本是希望声明一个暂时的变量,使得其类型和迭代器的类型value_type一致,但是现在被const修饰无法赋值,不满足需求,因此当迭代器是一个 pointer to const的类型,只能另外设计一个偏特化版本,让value type指向的是non const版本即可
template <class T>
struct iterator_traits<const T*>{//偏特化版本 当迭代器的类型是pointer-to-const的时候//萃取出来的型别是 T 而不是const Ttypedef T value_type;
};
- 这样的话 不管面对的是迭代器、原生指针int* 或者const int* 都可以通过traits 取出正确的 value type
- 使用traits 萃取各个迭代器的特性,这里特性是指 迭代器的相应的型别,但是需要每一个迭代器都需要使用内嵌型别定义的方式定义出相应的型别