STL源码剖析——iterators与trait编程#1 尝试设计一个迭代器

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

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

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

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

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

  略做简化:

 1 template <class X>
 2 class auto_ptr {
 3 private:
 4     X* ptr;
 5 public:
 6     typedef X element_type;
 7     explicit auto_ptr(X* p = 0) __STL_NOTHROW : ptr(p) {}
 8     auto_ptr(const auto_ptr& a) __STL_NOTHROW : ptr(a.ptr) {}
 9     template <class T>
10     auto_ptr& operator=(const auto_ptr<T>& a) __STL_NOTHROW {
11         if (&a != this) {
12             reset(a.release())
13         }
14         return *this;
15     }
16     ~auto_ptr() {
17             delete ptr;
18     }
19     X& operator*() const __STL_NOTHROW { return *ptr; }
20     X* operator->() const __STL_NOTHROW { return ptr; }
21     X* get() const __STL_NOTHROW { return ptr; }
22 };

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

 1 template <typename T>
 2 class List
 3 {
 4     void insert_front(T value);
 5     void insert_end(T value);
 6     void display(std::ostream &os = std::cout)const;
 7     //...
 8 private:
 9     ListItem<T>* _end;
10     ListItem<T>* _front;
11     long _size;
12 };
13
14 template <typename T>
15 class ListItem
16 {
17 public:
18     T value()const { return _value; }
19     ListItem* next()const { return _next; }
20     //...
21
22 private:
23     T _value;
24     ListItem* _next;    //单向链表
25 };

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

 1 template<typename Item>
 2 struct ListIter
 3 {
 4     Item *ptr;
 5
 6     ListIter(Item* p = 0) :ptr(p);
 7
 8     Item& operator*()const { return *ptr; }
 9     Item* operator->()const { return ptr; }
10
11     ListIter& operator++()
12     {
13         ptr = ptr->next(); return *this;
14     }
15     ListIter& operator++(int)
16     {
17         ListIter tmp = *this;
18         ++*this;
19         return tmp;
20     }
21     bool operator==(const ListIter& i)const
22     {
23         return this->ptr == i.ptr;
24     }
25     bool operator!=(const ListItem& i)consts
26     {
27         return !*this == i;
28     }
29 };

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

 1 void main()
 2 {
 3     List<int> mylist;
 4
 5     for (int i = 0; i < 5; i++)
 6     {
 7         mylist.insert_front(i);
 8         mylist.insert_end(i + 2);
 9     }
10     mylist.display();
11
12     ListIter<ListItem<int>>begin(mylist.front());
13     ListIter<ListItem<int>>end;
14     ListIter<ListItem<int>>iter;
15
16     iter = find(being, end, 3);
17     if (iter == end)
18         cout << "not found" << endl;
19     else
20         cout << "found: " << iter->value() << endl;
21
22     iter = find(begin, end, 7);
23     if (iter == end)
24         cout << "not found" << endl;
25     else
26         cout << "found: " << iter->value() << endl;
27 }

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

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

原文地址:https://www.cnblogs.com/MisakiJH/p/11666818.html

时间: 2024-10-07 09:30:34

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

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

在算法中运用迭代器时,很可能用到其相应类型.什么是相应类型?迭代器所指对象的类型便是其中一个.我曾有一个错误的理解,那就是认为相应类型就是迭代器所指对象的类型,其实不然,相应类型是一个大的类别,迭代器所指对象的类型只是里面的其中一个.后面会讨论到相应类型的另外几种. 假设算法需要声明一个变量,以“迭代器所指对象的类型”为类型,该怎么做?或许我们可能会想到RTTI性质中的typeid(),但获得的只是类型名称,并不能拿来声明变量. 其中一个解决方法是:利用模版函数中的参数推导(argument d

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

在前两节介绍了迭代器的五个相应类型,并讲述如何利用traits机制提取迭代器的类型,但始终是把iteartor_traits类分割开来讨论,这影响我们的理解,本节将给出iteator的部分源码,里面涵盖了整个iteartor_traits泛化版本.偏特化版本以及一些算法的完整代码.重新把先前讲的知识捋顺一下. 1 //节选自SGI STL<stl_iterator.h> 2 3 //五种迭代器类型,继承表示,越底层的基类越低级,越上层的子类越高级 4 struct input_iterator

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

最后一个迭代器的相应类型就是iterator_category,就是迭代器本身的类型,根据移动特性与实行的操作,迭代器被分为了五类: Input Iterator:这种迭代器所指的对象,不允许外界改变.只读(read only). Output Iterator:唯写(write only) Forward Iterator:允许写入型算法在此种迭代器所形成的区间上进行读写操作,具有Input迭代器的全部功能和Output迭代器的大部分功能.支持++运算符. Bidirectional Iter

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

上节给出了iterator_traits以及用到traits机制的部分函数的完整代码,可以看到traits机制能够提取迭代器的特性从而调用不同的函数,实现效率的最大化.显然这么好的机制不应该仅局限于在STL里面使用,在前某一节中我们也有说到,traits机制能够萃取类的特性,而这个类分为两个类别,一是迭代器类,二普通类,迭代器类我们已经有所了解了,那么本节就来学习负责萃取普通类型特性的__type_traits吧. 于普通类型而言,我们所关注的特性是指:这个类型是否具备non-trivial d

stl源码剖析学习笔记(二)traits编程技法简明例程

解释说明 traits侯捷老师的翻译是萃取.其目的就是在编译期进行模板调用的类型识别,从而做一些事情. 最突出的例子,我觉得不是<STL源码剖析>中"迭代器概念与traits编程技法"这一章的说明,而是stl算法中copy的实现.代码在stl源码的stl_algobase.h中. copy的最终实现,大致分为两类,一类是直接整块内存的memmove操作,另一类是一个个对象赋值.其中涉及has_trivial_assignment_operator的类型推断. 如果has_t

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

侯捷老师在<STL源码剖析>中说到:了解traits编程技术,就像获得“芝麻开门”的口诀一样,从此得以一窥STL源码的奥秘.如此一说,其重要性就不言而喻了.      之前已经介绍过迭代器,知道了不同的数据结构都有自己专属的迭代器,不同的迭代器也有不同的特性,由于算法的接口是统一的,通过迭代器的不同属性,算法自动选择正确的执行流程,在完全任务的同时,也尽可能提高算法的执行效率.那算法如何获知迭代器的属性呢?这一光荣的任务就是traits完成的.在STL实现中,traits编程技术得到大量的运用

[转载]《STL源码剖析》阅读笔记之 迭代器及traits编程技法

本文从三方面总结迭代器   迭代器的思想   迭代器相应型别及traits思想   __type_traits思想 一 迭代器思想 迭代器的主要思想源于迭代器模式,其定义如下:提供一种方法,使之能够依序巡防某个聚合物(容 器)所含的元素,而又无需暴露该聚合物的内部表达式.可见她的主要作用便是能够降低耦合,提高代码的 模块性. STL的的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一贴胶着剂将它们撮合 在一起,这贴胶着剂便是迭代器.迭代器的行为类似智能指针(例如标准库的auto_pt

《STL源码剖析》---stl_iterator.h阅读笔记

STL设计的中心思想是将容器(container)和算法(algorithm)分开,迭代器是容器(container)和算法(algorithm)之间的桥梁. 迭代器可以如下定义:提供一种方法,能够依序寻访某个容器内的所有元素,而又无需暴露该容器的内部表达方式. 在阅读代码之前,要先了解一个新概念:Traits编程技法 template <class T> struct MyIter { typedef T value_type //内嵌型别声明 T *ptr; MyIter(T *p = 0

STL&quot;源码&quot;剖析-重点知识总结

STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合套用: 容器(Containers):各种数据结构,如:vector.list.deque.set.map.用来存放数据.从实现的角度来看,STL容器是一种class template. 算法(algorithms):各种常用算法,如:sort.search.copy.erase.从实现的角度来看,STL算法