STL源码剖析——iterators与trait编程#1 尝试设计一个迭代器
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 尝试设计一个迭代器的更多相关文章
- STL源码剖析——iterators与trait编程#2 Traits编程技法
在算法中运用迭代器时,很可能用到其相应类型.什么是相应类型?迭代器所指对象的类型便是其中一个.我曾有一个错误的理解,那就是认为相应类型就是迭代器所指对象的类型,其实不然,相应类型是一个大的类别,迭代器 ...
- STL源码剖析——iterators与trait编程#4 iterator源码
在前两节介绍了迭代器的五个相应类型,并讲述如何利用traits机制提取迭代器的类型,但始终是把iteartor_traits类分割开来讨论,这影响我们的理解,本节将给出iteator的部分源码,里面涵 ...
- STL源码剖析——iterators与trait编程#3 iterator_category
最后一个迭代器的相应类型就是iterator_category,就是迭代器本身的类型,根据移动特性与实行的操作,迭代器被分为了五类: Input Iterator:这种迭代器所指的对象,不允许外界改变 ...
- STL源码剖析——Iterators与Traits编程#5 __type_traits
上节给出了iterator_traits以及用到traits机制的部分函数的完整代码,可以看到traits机制能够提取迭代器的特性从而调用不同的函数,实现效率的最大化.显然这么好的机制不应该仅局限于在 ...
- 《STL源码剖析》学习之traits编程
侯捷老师在<STL源码剖析>中说到:了解traits编程技术,就像获得“芝麻开门”的口诀一样,从此得以一窥STL源码的奥秘.如此一说,其重要性就不言而喻了. 之前已经介绍过迭代器 ...
- 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法
大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...
- STL源码剖析 迭代器(iterator)概念与编程技法(三)
1 STL迭代器原理 1.1 迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...
- STL源码剖析之序列式容器
最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
随机推荐
- [提权]sudo提权复现(CVE-2019-14287)
2019年10月14日, sudo 官方在发布了 CVE-2019-14287 的漏洞预警. 0x00 简介 sudo 是所有 unix操作系统(BSD, MacOS, GNU/Linux) 基本集成 ...
- Redis哨兵日常实践
一.日常操作 指定一个从做新主 有时候需要将当前主节点机器下线,并指定一个高一些性能的从节点接替 将其它从节点的slave-priority配置为0,然后在随意一台 Setinel 执行sentine ...
- tecplot当中共用一个legend进行对比
原版视频下载地址链接: https://pan.baidu.com/s/1nvHa0kx 密码: q33e
- 单一职责原则(SRP)
内聚性:一个模块的组成元素之间的功能相关性.就一个类而言,应该仅有一个引起它变化的原因.当需求变化时,该变化会反映为类的职责的变化,如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个.如果 ...
- essential
essential - 必应词典 美[ɪ'senʃ(ə)l]英[ɪ'senʃ(ə)l] n.要点:要素:实质:必需品 adj.完全必要的:必不可少的:极其重要的:本质的 网络基本的:必需的 变形复数: ...
- 《Linux设备驱动程序》第三版 scull编译 Ubuntu18.04
0 准备工作. 0.0 系统环境:Ubuntu18.04.1 amd64. 0.1 安装必要软件包 1 sudo apt install build-essential bison flex libs ...
- TextBox光标定位到文本末尾
private void RichTextBox1_TextChanged(object sender, EventArgs e) { this.richTextBox1.Select(richTex ...
- orchestrator的安装和配置
介绍 在MySQL高可用架构中,目前使用比较多的是Percona的PXC,Galera以及MySQL 5.7之后的MGR等,其他的还有的MHA,今天介绍另一个比较好用的MySQL高可用复制管理工具:O ...
- B2B2C 商业模式
b2b2c_百度百科https://baike.baidu.com/item/b2b2c/876805 What is Business to Business to Consumer (B2B2C) ...
- 用google translate大文件
问题: google translate对于大文件不支持,咋办? 思路:自己写个函数把的文件拆成小文件,再用google translate! code: from googletrans impor ...