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提供六大组件,彼此可以组合 ...
随机推荐
- vue的ref试用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- H5注意点(2)
1. 列表标签的作用:给一堆数据添加列表语义,也就是告诉搜索引擎告诉浏览器这一堆数据是一个整体. 2. HTML中列表标签的分类 - 无序列表(最多)(unordered list)(格式:ul> ...
- html5中output元素详解
html5中output元素详解 一.总结 一句话总结: output元素是HTML5新增的元素,用来设置不同数据的输出,没什么大用,了解即可 <form action="L3_01. ...
- keep-alive 实现从列表页到详情页,然后再回到列表页并保持原来列表页的页码数,并且只刷新数据
思路: keep-alive应用场景介绍 <keep-alive> 不会在函数式组件中正常工作,因为它们没有缓存实例.结合router,缓存部分页面 activated 和 deactiv ...
- SQL Server 将查询结果集以XML形式展现 for xml path
for xml path,其实它就是将查询结果集以XML形式展现 双击打开
- Centos 安装 nginx 特定版本
CentOS 6.9/7通过yum安装指定版本的Nginx - EasonJim - 博客园https://www.cnblogs.com/EasonJim/p/9020896.html [root@ ...
- flutter 右滑返回上一页
import 'package:flutter/material.dart'; import 'package:flutter_app/pages/SplashScreen.dart'; import ...
- Ionic app IOS 在Xcode 模拟运行 真机调试
1. 创建项目: sudo ionic start myApp tabs 2 cd 到刚才创建的项目 3. sudo ionic cordova platform add ios 把 ios 环境添加 ...
- shell编程系列25--shell操作数据库实战之备份MySQL数据,并通过FTP将其传输到远端主机
shell编程系列25--shell操作数据库实战之备份MySQL数据,并通过FTP将其传输到远端主机 备份mysql中的库或者表 mysqldump 常用参数详解: -u 用户名 -p 密码 -h ...
- 五、postman-sandbox
一.在postman中运行一些JavaScript代码的地方 公共库(javascript) 环境变量与全局变量 动态变量 操作cookie 获取和查看请求及响应 读取数据文件 二.api文档 htt ...