STL的中心思想在于:将数据容器与算法分开,独立设计,再用一帖粘着剂将它们撮合在一起。而扮演粘着剂这个角色的就是迭代器。容器和算法泛型化,从技术角度来看并不困难,C++的模板类和模板函数可分别达成目标,但如何设计出两者之间良好的粘着剂,才是大难题。

  我们可以来尝试一下自己设计一个迭代器,看途中会遇到一些什么样的难题,同时看看SGI STL是怎么解决它们的。不过在此之前,我们可以先使用一下迭代器,看看其有什么样的功能,以find()函数为例:

 //来自SGI <stl_algo.h>
template <class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value) {
while (first != last && *first != value) ++first;
return first;
}

  只要给予不同的迭代器,find()便能够对不同的容器进行查找操作,那么按照这个说法,迭代器似乎是依附在容器之下的,而且在函数中提取了迭代器指向的内容(*firsst),这给我们提供一点设计思路:

迭代器是一种行为类似指针的对象,而指针的各种行为中最常见的就也是最重要的便是内容提取和成员访问,因此,迭代器最重要的工作就是对operator *和operator ->进行重载。关于这一点,我们可以参考一下C++98智能指针auto_ptr是怎么设计的,虽然是已经被摒弃掉的玩意,但是看看也无妨,至于智能指针是干嘛用的,这里就不在介绍。

  略做简化:

 template <class X>
class auto_ptr {
private:
X* ptr;
public:
typedef X element_type;
explicit auto_ptr(X* p = ) __STL_NOTHROW : ptr(p) {}
auto_ptr(const auto_ptr& a) __STL_NOTHROW : ptr(a.ptr) {}
template <class T>
auto_ptr& operator=(const auto_ptr<T>& a) __STL_NOTHROW {
if (&a != this) {
reset(a.release())
}
return *this;
}
~auto_ptr() {
delete ptr;
}
X& operator*() const __STL_NOTHROW { return *ptr; }
X* operator->() const __STL_NOTHROW { return ptr; }
X* get() const __STL_NOTHROW { return ptr; }
};

  有了模仿对象,现在我们可以来为list设计一个迭代器,但并不是STL里那个复杂的list,而是自己简单设计的一个list:

 template <typename T>
class List
{
void insert_front(T value);
void insert_end(T value);
void display(std::ostream &os = std::cout)const;
//...
private:
ListItem<T>* _end;
ListItem<T>* _front;
long _size;
}; template <typename T>
class ListItem
{
public:
T value()const { return _value; }
ListItem* next()const { return _next; }
//... private:
T _value;
ListItem* _next; //单向链表
};

  如果我们想对List这个容器使用find()函数,就要为其设计一个迭代器。当我们提取这个迭代器的内容时,返回的应该是一个ListItem对象;当我们递增迭代器时,它应该指向下一个ListItem对象。为了让该迭代器适用于任何类型的结点,而不只限于ListItem对象,我们可以将它设计为一个模板类:

 template<typename Item>
struct ListIter
{
Item *ptr; ListIter(Item* p = ) :ptr(p); Item& operator*()const { return *ptr; }
Item* operator->()const { return ptr; } ListIter& operator++()
{
ptr = ptr->next(); return *this;
}
ListIter& operator++(int)
{
ListIter tmp = *this;
++*this;
return tmp;
}
bool operator==(const ListIter& i)const
{
return this->ptr == i.ptr;
}
bool operator!=(const ListItem& i)consts
{
return !*this == i;
}
};

  其实可能从上面就能看出一些问题了,看似是针对List而设计的迭代器,但在实际使用时,我们必须把List的结点类(ListItem)而非List本身作为类型参数传给ListIter才能正常操作:

 void main()
{
List<int> mylist; for (int i = ; i < ; i++)
{
mylist.insert_front(i);
mylist.insert_end(i + );
}
mylist.display(); ListIter<ListItem<int>>begin(mylist.front());
ListIter<ListItem<int>>end;
ListIter<ListItem<int>>iter; iter = find(being, end, );
if (iter == end)
cout << "not found" << endl;
else
cout << "found: " << iter->value() << endl; iter = find(begin, end, );
if (iter == end)
cout << "not found" << endl;
else
cout << "found: " << iter->value() << endl;
}

  为了完成一个针对List而设计的迭代器,我们无可避免地暴露了太多List的实现细节:在main()之中为了制作begin和end两个迭代器,我们暴露了链表结点类ListItem;在迭代器类的设计中我们暴露了ListItem的操作函数next()。如果不是为了迭代器,结点类应该完全隐藏起来,于真正的List而言,我们也没见过其背后结点类的任何细节。换句话说,要设计出ListIter,首先必须对List的实现细节有非常丰富的了解。既然如此,干脆就把迭代器的开发工作交给List的设计者好了,如此一来,所有的实现细节都能够被封装起来不被使用者看到,这也是为什么每一种STL容器都提供了专属的迭代器。

  本节参考了智能指针auto_ptr设计了一个针对List的迭代器,发现了如果要想在迭代器的使用中不暴露容器的实现细节,就必须把迭代器设计工作交给容器的开发者。日后在学习各容器时都会对其特定的迭代器做一定的总结。

STL源码剖析——iterators与trait编程#1 尝试设计一个迭代器的更多相关文章

  1. STL源码剖析——iterators与trait编程#2 Traits编程技法

    在算法中运用迭代器时,很可能用到其相应类型.什么是相应类型?迭代器所指对象的类型便是其中一个.我曾有一个错误的理解,那就是认为相应类型就是迭代器所指对象的类型,其实不然,相应类型是一个大的类别,迭代器 ...

  2. STL源码剖析——iterators与trait编程#4 iterator源码

    在前两节介绍了迭代器的五个相应类型,并讲述如何利用traits机制提取迭代器的类型,但始终是把iteartor_traits类分割开来讨论,这影响我们的理解,本节将给出iteator的部分源码,里面涵 ...

  3. STL源码剖析——iterators与trait编程#3 iterator_category

    最后一个迭代器的相应类型就是iterator_category,就是迭代器本身的类型,根据移动特性与实行的操作,迭代器被分为了五类: Input Iterator:这种迭代器所指的对象,不允许外界改变 ...

  4. STL源码剖析——Iterators与Traits编程#5 __type_traits

    上节给出了iterator_traits以及用到traits机制的部分函数的完整代码,可以看到traits机制能够提取迭代器的特性从而调用不同的函数,实现效率的最大化.显然这么好的机制不应该仅局限于在 ...

  5. 《STL源码剖析》学习之traits编程

    侯捷老师在<STL源码剖析>中说到:了解traits编程技术,就像获得“芝麻开门”的口诀一样,从此得以一窥STL源码的奥秘.如此一说,其重要性就不言而喻了.      之前已经介绍过迭代器 ...

  6. 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...

  7. STL源码剖析 迭代器(iterator)概念与编程技法(三)

    1 STL迭代器原理 1.1  迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...

  8. STL源码剖析之序列式容器

    最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...

  9. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

随机推荐

  1. vue element-UI Form表单验证

    摘自官网 https://element.eleme.cn/#/zh-CN/component/form 保证prop的值等于v-model的值,并且初始化值,这样验证才好使. 可以自定义验证 < ...

  2. ubuntu16.04安装python3.7

    1.安装依赖包 sudo apt-get update sudo apt-get install build-essential python-dev python-setuptools python ...

  3. sourceinsight4 用设置

    通用设置Options - File Type Options: 显示行号:勾选Show line mumbers选中自动高亮:勾选Hightlight references to selected ...

  4. (基因功能 & 基因表达调控)研究方案

    做了好久的RNA-seq分析,基因表达也在口头溜了几年了,但似乎老是浮在表面. 对一件事的了解程度决定了你的思维深度,只想做技工就不用想太多,想做大师就一定要刨根问底. 老是说基因表达,那么什么是基因 ...

  5. Spring’s RestTemplate

    Spring’s RestTemplate /** * After the word document is generated in memory we can upload it to the s ...

  6. No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK? idea maven 打包报错问题解决

    mvn clean install -X -Dmaven.test.skip=true -P dev 打包报错:No compiler is provided in this environment. ...

  7. 将移远通信的EC20驱动移植到NUC972上(转)

    源: 将移远通信的EC20驱动移植到NUC972上

  8. Cannot find module 'laravel-elixir'问题解决方法

    在用gulp 安装elixir的时候报了这样的错误: Laravel elixir npm error Cannot find module 'laravel-elixir/ingredients/c ...

  9. ISO/IEC 9899:2011 条款6.7.3——类型限定符

    6.7.3 类型限定符 语法 1.type-qualifier: const restrict volatile _Atomic 约束 2.除了指针类型(其被引用的类型是一个对象类型)之外的类型,不应 ...

  10. selenium grid用法

    一,selenium grid是什么? selenium是一个用于UI自动化测试的工具. selenium grid是selenium家族中的三大组件的一员.selenium grid有两个版本,gr ...