STL源码剖析(适配器)
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源码剖析(适配器)的更多相关文章
- STL源码剖析 迭代器(iterator)概念与编程技法(三)
1 STL迭代器原理 1.1 迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...
- STL源码剖析之序列式容器
最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
- 【转载】STL"源码"剖析-重点知识总结
原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...
- (原创滴~)STL源码剖析读书总结1——GP和内存管理
读完侯捷先生的<STL源码剖析>,感觉真如他本人所说的"庖丁解牛,恢恢乎游刃有余",STL底层的实现一览无余,给人一种自己的C++水平又提升了一个level的幻觉,呵呵 ...
- 《STL源码剖析》环境配置
首先,去侯捷网站下载相关文档:http://jjhou.boolan.com/jjwbooks-tass.htm. 这本书采用的是Cygnus C++ 2.91 for windows.下载地址:ht ...
- STL源码剖析读书笔记之vector
STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...
- STL"源码"剖析
STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略 ...
- 《STL源码剖析》相关面试题总结
原文链接:http://www.cnblogs.com/raichen/p/5817158.html 一.STL简介 STL提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...
随机推荐
- Spring的消息 Java Message Service (JMS)
Spring有两种方法提供对EJB的支持: Spring能让你在Spring的配置文件里,把EJB作为Bean来声明.这样,把EJB引用置入到其他Bean的属性里就成为可能了,好像EJB就是另一个P ...
- 【二分】Subsequence
[POJ3061]Subsequence Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 15908 Accepted: ...
- FZU 2105 Digits Count(按位维护线段树)
[题目链接] http://acm.fzu.edu.cn/problem.php?pid=2105 [题目大意] 给出一个序列,数字均小于16,为正数,每次区间操作可以使得 1. [l,r]区间and ...
- 【数论】【中国剩余定理】poj1006 生理周期
CRT用于求解一元线性同余方程组(模数互质),实际上模数不互质我们也可以解决,在之前的某篇文章里提过.如下 http://www.cnblogs.com/autsky-jadek/p/6596010. ...
- 【序列莫队】BZOJ2038- [2009国家集训队]小Z的袜子(hose)
[题目大意]给出1-N只袜子的颜色,多次询问L-R中选出一双同色袜子的概率. [思路] 裸莫队.基本的莫队步骤:①分组(每组大小为根号sqrt(n),共sqrt(n)组)②排序(左边界分组,右边界在组 ...
- [DesignPattern]Builder设计模式
模式的定义 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 模式的使用场景 相同的方法,不同的执行顺序,产生不同的事件结果时: 多个部件或零件,都可以装配到一个对象中,但是 ...
- ncnn阅读 - CMakeLists.txt
CMAKE_TOOLCHAIN_FILE This variable is specified on the command line when cross-compiling with CMake. ...
- Everything | 本地文件搜索引擎工具
文章目录 本地文件搜索软件 下载地址 注意 本地文件搜索软件 您是否觉得 Windows 自带的文件搜索比较慢?! 今天,给大家分享一款超快好用的本地文件搜索软件:Everything Search ...
- CentOS 6.9下配置安装KVM
注意:KVM一切安装和运行都是在root用户下完成的,并且只有root才能支持某些软件. 一.准备工作: 1.查看系统版本.内核版本 ##查看系统版本 # cat /etc/redhat-releas ...
- http-server 超轻量级web服务器
有的时候做前端,想要运行一些代码,但是又没有必要使用tomcat或者Apache http server,这个时候一个轻量级的简单的http server就可以搞定了. Http-server是基于n ...