STL中由三类适配器,它们分别是:

1.容器适配器(stack、queue)

2.迭代器适配器(insert_iterator、reverse_iterator、iostream_iterator)

3.函数适配器(bind1st等等)

容器适配器

关于容器适配器我们已经在前面的http://www.cnblogs.com/runnyu/p/6003821.html讲过了。

迭代器适配器

1.insert iterator

当我们这样使用copy算法的时候:

 vector<int> ins = { , , , };
vector<int> coll; // coll为空
copy(ins.begin(), ins.end(), coll.begin());

毫无疑问会出现错误,因为copy算法中调用的是iterator的operator*跟operator=,用的是赋值操作,而要进行赋值的iterator并不合法。

insert iterator可以解决这个问题,其实它的实现很简单。

下面是back_inserter的用法跟实现,它的实现主要是重载了operator*跟operator=方法,然后提供了一个接口函数。

 // 以容器为参数  将元素copy到coll的末尾
copy(ins.begin(), ins.end(), back_inserter(coll)); template <class Container>
class back_insert_iterator {
protected:
Container* container;
public:
typedef output_iterator_tag iterator_category; // output迭代器
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference; explicit back_insert_iterator(Container& x) : container(&x) {} // 重载operator= 改用push_back
back_insert_iterator<Container>&
operator=(const typename Container::value_type& value) {
container->push_back(value);
return *this;
}
back_insert_iterator<Container>& operator*() { return *this; }
back_insert_iterator<Container>& operator++() { return *this; }
back_insert_iterator<Container>& operator++(int) { return *this; }
}; // 对外提供了该接口
template <class Container>
inline back_insert_iterator<Container> back_inserter(Container& x) {
return back_insert_iterator<Container>(x);
}

front_inserter的用法跟实现类似,只是用push_front代替push_back而已。

inserter的用法跟实现只是比前面两个多了一个指定位置的迭代器而已。

 template <class Container>
class insert_iterator {
protected:
Container* container;
// 其成员多了一个迭代器
typename Container::iterator iter;
public:
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference; insert_iterator(Container& x, typename Container::iterator i)
: container(&x), iter(i) {} // 改用insert方法
insert_iterator<Container>&
operator=(const typename Container::value_type& value) {
iter = container->insert(iter, value);
++iter;
return *this;
}
insert_iterator<Container>& operator*() { return *this; }
insert_iterator<Container>& operator++() { return *this; }
insert_iterator<Container>& operator++(int) { return *this; }
}; // 对外提供的接口
template <class Container, class Iterator>
inline insert_iterator<Container> inserter(Container& x, Iterator i) {
typedef typename Container::iterator iter;
return insert_iterator<Container>(x, iter(i));
}

2.reserve iterator

容器的实现中的rbegin()跟rend()就是使用的是reserve_iterator。

为了能够兼容算法,进行反向遍历,reserver iterator的operator++跟operator--应该跟普通迭代器的行为相反。

 class vector
{
//...
// 使用的就是reverse_iterator
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
// ...
}; // reserve_iterator的实现
template <class Iterator>
class reverse_iterator
{
protected:
Iterator current;
public:
// ...
typedef Iterator iterator_type;
typedef reverse_iterator<Iterator> self; // 构造函数
explicit reverse_iterator(iterator_type x) : current(x) {} // 将迭代器前移一步 才能保持跟正向迭代器的一切惯常行为
// 如rbegin()就是end() - 1 指向反向的第一个可用元素
reference operator*() const {
Iterator tmp = current;
return *--tmp;
} // operator++跟operator--跟普通迭代器行为相反
// 使得reverse_iterator能进行反向遍历
self& operator++() {
--current;
return *this;
}
self& operator--() {
++current;
return *this;
}
// ... 还重载了operator+=、operator-=等等
};

3.iostream iterator

iostream_iterator内部都维护一个流对象。

先看看istream_iterator的用法:

 // 先看看istream_iterator的用法
vector<int> coll;
// 调用的是无参构造函数(end_marker = false)
istream_iterator<int> end;
copy(istream_iterator<int>(cin), end, back_inserter(coll)); // 然后看看它调用的copy版本
// 可见istream_iterator需要重载operator==、operator*、operator++等等操作符
for ( ; first != last; ++result, ++first)
*result = *first;
return result; // 结束条件为first != last
// 因此istream_iterator需要重载operator== 这是一个友元函数
// 根据end_marker(为false表示读到eof或者读到不符合要求的数据)进行判断
// 因此调用默认构造函数的istream_iterator可以当做end()
template <class T,end_marker class Distance>
inline bool operator==(const istream_iterator<T, Distance>& x,
const istream_iterator<T, Distance>& y) {
return x.stream == y.stream && x.end_marker == y.end_marker ||
x.end_marker == false && y.end_marker == false;
}

现在再看istream_iterator的实现就比较简单了:

 template <class T, class Distance = ptrdiff_t>
class istream_iterator {
protected:
istream* stream;
T value;
bool end_marker; // 读到eof或者不符合要求的数据时将end_marker设置为false
void read() {
end_marker = (*stream) ? true : false;
// 从istream中读取一个值并复制给value
if (end_marker) *stream >> value;
end_marker = (*stream) ? true : false;
}
public:
typedef input_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef const T* pointer;
typedef const T& reference; // 无参构造函数 这个是结束的关键
istream_iterator() : stream(&cin), end_marker(false) {} // 调用read() 程序将等待输入
istream_iterator(istream& s) : stream(&s) { read(); } // 取出当前读到的元素
reference operator*() const { return value; } // 实际上调用的是read() 每次调用operator++都等待用户输入
istream_iterator<T, Distance>& operator++() {
read();
return *this;
}
};

而作为output iterator的ostream_iterator则是维护一个ostream member,重载其operator=方法

直接看它的用法跟实现:

 list<int> coll = { , , , ,  };
// ostream_iterator接受一个ostream对象跟一个分隔符
copy(coll.begin(), coll.end(), ostream_iterator<int>(cout, " ")); // ostream_iterator的实现
template <class T>
class ostream_iterator {
protected:
ostream* stream;
// 分隔符
const char* string;
public:
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference; ostream_iterator(ostream& s) : stream(&s), string() {}
ostream_iterator(ostream& s, const char* c) : stream(&s), string(c) {} // 重载operator= 首先输出value 然后输出分隔符
ostream_iterator<T>& operator=(const T& value) {
*stream << value;
if (string) *stream << string;
return *this;
}
// 返回该对象(结合copy的*result=理解)
ostream_iterator<T>& operator*() { return *this; }
ostream_iterator<T>& operator++() { return *this; }
ostream_iterator<T>& operator++(int) { return *this; }
};

函数适配器

前面已经说过函数适配器bind2nd的实现:http://www.cnblogs.com/runnyu/p/6010101.html。

其实函数适配器都是在构造的时候将仿函数跟需要的参数设置为其成员,然后重载operator()对其成员进行修饰。

还有值得一看的是ptr_fun跟mem_fun的实现,它们可以将普通/成员函数变成一个仿函数,

其实实现就是该function object拥有一个函数指针,并重载operator()方法而已。

ptr_fun实现比较简单:

 template <class Arg, class Result>
class pointer_to_unary_function : public unary_function<Arg, Result> {
protected:
// 拥有其函数指针 该函数参数数量为1
Result (*ptr)(Arg);
public:
pointer_to_unary_function() {}
explicit pointer_to_unary_function(Result (*x)(Arg)) : ptr(x) {}
// 重载operator() 使得该对象为function object
Result operator()(Arg x) const { return ptr(x); }
}; // 对外提供的接口
template <class Arg, class Result>
inline pointer_to_unary_function<Arg, Result> ptr_fun(Result (*x)(Arg)) {
return pointer_to_unary_function<Arg, Result>(x);
}

mem_fun有多个版本,下面给出一种(无参数、通过pointer调用、non-const成员函数)的实现:

 template <class T>
class mem_fun_t<void, T> : public unary_function<T*, void> {
public:
explicit mem_fun_t(void (T::*pf)()) : f(pf) {}
// 重载operator() 使其变成一个function object
// 成员函数默认带一个this指针
void operator()(T* p) const { (p->*f)(); }
private:
void (T::*f)(); // f为其成员函数指针
}; // 对外的接口
template <class S, class T>
inline mem_fun_t<S,T> mem_fun(S (T::*f)()) {
return mem_fun_t<S,T>(f);
}

下面给出该适配器的用法:

 class Sharp {
public:
virtual void name() { cout << "sharp" << endl; }
}; class Triangle
: public Sharp {
public:
virtual void name() { cout << "triangle" << endl; }
}; class Rectangle
: public Sharp {
virtual void name() { cout << "rectangle" << endl; }
}; int main(int argc, char *argv[])
{
vector<Sharp*> coll;
coll.push_back(new Sharp());
coll.push_back(new Triangle());
coll.push_back(new Rectangle());
for_each(coll.begin(), coll.end(), mem_fun(&Sharp::name));
return ;
}

STL源码剖析(适配器)的更多相关文章

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

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

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

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

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

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

  4. 【转载】STL"源码"剖析-重点知识总结

    原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...

  5. (原创滴~)STL源码剖析读书总结1——GP和内存管理

    读完侯捷先生的<STL源码剖析>,感觉真如他本人所说的"庖丁解牛,恢恢乎游刃有余",STL底层的实现一览无余,给人一种自己的C++水平又提升了一个level的幻觉,呵呵 ...

  6. 《STL源码剖析》环境配置

    首先,去侯捷网站下载相关文档:http://jjhou.boolan.com/jjwbooks-tass.htm. 这本书采用的是Cygnus C++ 2.91 for windows.下载地址:ht ...

  7. STL源码剖析读书笔记之vector

    STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...

  8. STL"源码"剖析

    STL"源码"剖析-重点知识总结   STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略 ...

  9. 《STL源码剖析》相关面试题总结

    原文链接:http://www.cnblogs.com/raichen/p/5817158.html 一.STL简介 STL提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...

随机推荐

  1. Flask实战第52天:cms添加轮播图前端代码逻辑完成

    首页我们在模态框中的保存按钮加一个id,这样方便我们选取这个按钮 <button type="button" class="btn btn-primary" ...

  2. gwy常识

    其实公务员考试是一门艺术,七分靠水平,三分凭发挥,充分而又细致的准备则是取得优秀成绩的前提.考生若想在笔试中成功上岸,还需苦练内功,凭技巧和真才实学在考场上一较高下.那么针对历年上海公务员考试笔试考情 ...

  3. 【20181019T3】比特战争【最小生成树思想】

    题面 [错解] Hmm不可做啊 要不按b排个序? 然后并查集瞎搞,刷刷刷过了样例 然后大样例大了几万倍 出了组小数据,Successful Hack 弃疗 水过10分 [正解] 用占领的边将顶点连起来 ...

  4. BZOJ 2049 [Sdoi2008]Cave 洞穴勘测(动态树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2049 [题目大意] 要求支持树的断边和连边,以及连接查询 [题解] LCT练习题 [代 ...

  5. [AGC012E]Camel and Oases

    题意:有$n$个数轴上的绿洲,给定它们的坐标,有一只骆驼想要访问所有绿洲,当它的驼峰容量为$V$时,它可以走到和当前绿洲距离$\leq V$的绿洲,并可以继续走,它也可以用一次跳跃到达任意一个绿洲,只 ...

  6. 【朱-刘算法】【最小树形图】hdu6141 I am your Father!

    题意:给你一张带权有向图,让你求最大树形图.并在此前提下令n号结点父亲的编号最小. 比赛的时候套了个二分,TLE了. 实际上可以给每个边的权值乘1000,对于n号结点的父边,加上(999-父结点编号) ...

  7. 20172333 2017-2018-2 《Java程序设计》第8周学习总结

    20172333 2017-2018-2 <Java程序设计>第8周学习总结 教材学习内容 多态性应用可以随时间变化指向不同类型的对象. 多态性应用,方法的引用与方法的定义代码的绑定在运行 ...

  8. FreeMarker输出$

    FreeMarker如何输出$(美元符号)   使用${'$'} 如需要输出${user.id} 则${'$'}{user.id}

  9. iOS 自定义对象及子类及模型套模型的拷贝、归档存储的通用代码

    一.runtime实现通用copy 如果自定义类的子类,模型套模型你真的会copy吗,小心有坑. copy需要自定义类继承NSCopying协议 #import <objc/runtime.h& ...

  10. 【UEditor】关于导入ueditor-1.1.3.jar的问题---

    最近做一个项目,使用了百度的富文本框,本来是很简便易用的东西,在回显给富文本框的时候, var ue = UE.getEditor('container'); var noticeContent = ...