最近在看STL源码解析的迭代器(iterators)一章,涉及到c++ Traits的编程技法,刚开始看时一头雾水,反复看了好几遍之后才理解这个东西,因此来写写在这方面的理解,如有错误,希望读者指正。

1.迭代器(iterators)

在设计模式中,将迭代器进行如下定义:提供一种方法,使之能够依序寻访某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表达方式。

2.迭代器的设计思维

STL的设计思想在于:将容器和算法分离开来,彼此独立设计,最后在以一帖胶着剂撮合在一起,这个胶着剂就是迭代器。看下图

难题在于如何设计容器和算法之间良好的胶着剂,也就是迭代器。

3.迭代器是一种smart point

迭代器的行为类似指针的对象,而指针最重要的作用是内容提取(deference)和成员访问(member access)。因此,迭代器最重要的编程工作就是operator * 和operator->进行重载。关于这一点,c++标准库有一个auto_ptr可供参考,这是一个包装原生指针的对象,借此可以防止内存泄露(简单来说,使用auto_ptr,不需要delete)

void func()
{
auto_ptr<string> ps(new string("kobe")); cout << *ps << endl; //输出kobe
cout << ps->size() << endl; //输出5
//离开前不需要delete,auto_ptr会自动释放内存
}

对auto_ptr中的代码进行分析,这里有一份简化的版本:

 template<class T>
class auto_ptr
{
public:
explicit auto_ptr(T *p = ) :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& operate*() const { return *pointee; }
T* operate->() const { return pointee; }
T* get() const { return pointee; } ...
private:
T* pointee; };

对上述代码的简单分析:

auto_ptr类中,封装了一个原声指针pointee,通过第5行的构造函数进行初始化,然后在析构函数中,去delete pointee,这样就可以避免用户忘记delete而造成的内存泄露。

auto_ptr中还重载了*和->运算符,实现指针的取值和调用操作,很容易理解,值得注意的是auto_ptr中定义了两个成员函数模板(member template)。第一个是一个构造函数,它接受一个用U进行实例化的auto_ptr对象作为传参,我去查看了auto_ptr的源码,这里的release函数的作用是释放指针,并返回该指针值(假设有一个auto_ptr 对象rhs,rhs.release()作用是返回rhs.pointee的值,然后将rhs.pointee置空,返回的rhs.pointee用来初始化这里的对象的pointee,这样保证了,一块内存区域只有一个指针指向它,避免在析构的时候对该指针进行多次delete而出现错误)。第二个是赋值运算符重载(11~15行),调用reset函数(作用是首先将自己pointee所指向的内存释放,然后重置pointee为rhs.pointee,完成赋值)。

现在来为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,首先我们需要为它封装一个迭代器(行为类似指针的外衣),当我们引用提领(deference)这一个迭代器时,传回一个ListItem的对象;当我们递增该迭代器时,它应该指向下一个ListItem对象。为了让该迭代器试用与任何形态的节点,为它设计一个class template

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

使用迭代器的算法find的函数模板如下:

template <class InputIterator,class T>
InputIterator find(InputIterator first, InputIterator last, const T& value)
{
while (first != last && *first != value)
{
++first;
}
return first;
}

现在我们可以将List和find()通过ListIter粘合起来

void main()
{
List<int> mylist;
for (int i = ; i < ; i++)
{
mylist.insert_front(i);
mylist.insert_end(i+);
}
mylist.display(); //10(4 3 2 1 0 2 3 4 5 6)
ListIter<ListItem<int> > begin(mylist._front());
ListIter<ListItem<int> > end;
ListIter<ListItem<int> > iter;
iter = find(begin, end, );
if (iter == end)
cout << "not found" << endl;
else
cout << "found " << iter->value() << endl;
}

由于find函数内以*iter!=value来判断元素值是否吻合,而本例中value的型别是int,iter的型别是ListItem<int>,两者之间并无可使用的operator!=  ,所以这里必须设计一个全局的operator!=重载函数:

template <typename T>
bool operator!=(const ListItem<T>& item, T n)
{
return item.value() != n;
}

从以上的实现可以看出,为了完成一个针对List而设计的迭代器,我们无可避免的暴漏了太多的List实现细节:在main函数中为了制作begin和end,我们暴露了ListItem;在ListIter class中为了达成operator++的目的,我们暴露了ListItem的操作函数next()。

如果不是为了实现迭代器,listItem原本应该隐藏起来不曝光的。换句话说,要设计出ListIter首先必须对List的实现细节有非常丰富的了解,那么干脆把迭代器的开发工作直接交给List的设计者好了,如此一来,所有实现细节反而得以封装不被使用者看到,这就是为什么每一种STL容器都提供专属的迭代器的缘故。

STL迭代器------Traits编程技法详细理解(一)的更多相关文章

  1. STL之traits编程技法

    traits编程技法利用了“内嵌型别”的编程技巧与编译器的template参数推导功能. 下面主要看看利用traits编程技法实现的迭代器萃取机制. 5种迭代器类型定义: struct input_i ...

  2. STL源码分析读书笔记--第三章--迭代器(iterator)概念与traits编程技法

    1.准备知识 typename用法 用法1:等效于模板编程中的class 用法2:用于显式地告诉编译器接下来的名称是类型名,对于这个区分,下面的参考链接中说得好,如果编译器不知道 T::bar 是类型 ...

  3. STL——迭代器与traits编程技法

    一.迭代器 1. 迭代器设计思维——STL关键所在 在<Design Patterns>一书中对iterator模式定义如下:提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素 ...

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

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

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

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

  6. STL源码--iterator和traits编程技法

    第一部分 iterator学习 STL iterators定义: 提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表达方式. 任何iteartor都应该提供5 ...

  7. STL Traits编程技法

    traits编程技法大量运用于STL实现中.通过它在一定程度上弥补了C++不是强型别语言的遗憾,增强了C++关于型别认证方面的能力. traits编程技法是利用“内嵌型别”的编程技法和编译器的temp ...

  8. STL中的Traits编程技法

    最近在看读<STL源码剖析>,看到Traits编程技法这节时,不禁感慨STL源码作者的创新能力.那么什么是Traits编程技法呢?且听我娓娓道来: 我们知道容器的许多操作都是通过迭代器展开 ...

  9. 迭代器(iterator) 与 traits 编程技法

    看了候哥的<STL源码剖析>的迭代器那一章,在这里将思路稍微疏理一下 迭代器 迭代器模式的定义:提供一种方法,在不需要暴露某个容器的内部表现形式情况下,使之能依次访问该容器中的各个元素. ...

随机推荐

  1. 【Linux】 CentOS6.5安装Python2.7以及pip等工具

    原文地址 CentOS6.5下是原来就有python的,我的镜像里面自带的python版本是2.6.6.如果想要自己更新一个更加新的python版本可以这么做: 安装python2.7安装包. 从官网 ...

  2. mysql的备份脚本

    mysql的备份脚本 脚本如下 #!/bin/sh # mysql_backup.sh: backup mysql databases and keep newest 5 days backup. # ...

  3. Loadrunner使用时IE浏览器打不开怎么办

    1.ie浏览器去掉启用第三方浏览器扩展 2.loadrunner11 键盘F4,在browser Emulation点击change,在弹出的提示框中Browser version 选择8.0,pla ...

  4. NODE_ENV 不是内部或外部命令,也不是可运行的程序,或者批处理文件

    今天碰到一个奇葩问题,mac上能执行的npm命令,到windows上执行不聊了,报这个错 NODE_ENV 不是内部或外部命令,也不是可运行的程序,或者批处理文件 这是怎么回事呢?听我慢慢道来. &q ...

  5. DFA算法的简单说明!

    1.DFA算法简介 DFA全称为:Deterministic Finite Automaton,即确定有穷自动机.其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号 ...

  6. 【Alpha版本】冲刺阶段 - Day1 - 启航

    Alpha 阶段成员分工及任务量 成员 分工 任务量(小时) 袁逸灏 完成app用户车辆,子弹发射,背景移动,暂停界面,音乐界面,音乐查找,音乐播放 25 刘伟康 项目进度把控.分配任务.组织会议.整 ...

  7. RE:1054652545 - 论自己是如何蠢死的

    1.Java web 项目中 login/list 文件夹中return "login/list" 反复读取不到对应的jsp文件 一周后检查出来的原因上一级文件夹login前面多出 ...

  8. css3动画transition详解

    一.transition-property 语法: transition-property : none | all | [ <IDENT> ] [ ',' <IDENT> ] ...

  9. 【技巧】Java工程中的Debug信息分级输出接口及部署模式

    也许本文的标题你们没咋看懂.但是,本文将带大家领略输出调试的威力. 灵感来源 说到灵感,其实是源于笔者在修复服务器的ssh故障时的一个发现. 这个学期初,同袍(容我来一波广告产品页面,同袍官网)原服务 ...

  10. 18-TypeScript模板方法模式

    在有些情况下,一个功能在基础功能上是不会变的,算法的基本骨架也是确定的,但是在某些场景下算法的具体实现有些差异.应对这种问题,可以采用模板方法模式: abstract class Salary{ ab ...