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提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...
随机推荐
- CSS 笔记——定位尺寸
3. 定位尺寸 -> 尺寸 (1)height 基本语法 height : auto | length 语法取值 auto : 默认值.无特殊定位,根据HTML定位规则分配 length : 由 ...
- [BZOJ3622]已经没有什么好害怕的了(容斥DP)
给定两个数组a[n]与b[n](数全不相等),两两配对,求“a比b大”的数对比“b比a大”的数对个数多k的配对方案数. 据说做了这题就没什么题好害怕的了,但感觉实际上这是一个套路题,只是很难想到. 首 ...
- JZYZOJ1518 [haoi2011]b 莫比乌斯反演 分块 容斥
http://172.20.6.3/Problem_Show.asp?id=1518最开始只想到了n^2的写法,肯定要超时的,所以要对求gcd的过程进行优化.首先是前缀和容斥,很好理解.第二个优化大致 ...
- HDU 6039 Gear Up(线段树+并查集)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6039 [题目大意] 给出一些齿轮,有些齿轮是边相连,也就是拥有相同的线速度, 有的齿轮是轴相连,也 ...
- bzoj 3728: PA2014Final Zarowki
3728: PA2014Final Zarowki Description 有n个房间和n盏灯,你需要在每个房间里放入一盏灯.每盏灯都有一定功率,每间房间都需要不少于一定功率的灯泡才可以完全照亮.你可 ...
- 零起点学算法08——简单的输入和计算(a+b)
#include <stdio.h> int main() { int a; int b; scanf("%d %d",&a,&b); printf(& ...
- 既然有文件后缀名,为何还需要MIME类型?
作者:Vincross链接:https://www.zhihu.com/question/60495696/answer/204530120来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商 ...
- HDU 4633 Who's Aunt Zhang (2013多校4 1002 polya计数)
Who's Aunt Zhang Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- 通配置文件的方式控制java.util.logging.Logger日志输出
转自:http://zochen.iteye.com/blog/616151 简单的实现了下利用JDK中类java.util.logging.Logger来记录日志.主要在于仿照log4j方式用配置文 ...
- jQuery中,选择器既匹配开头又匹配结尾
jQuery中,选择器既匹配开头又匹配结尾的方法: [attr^=val]attr$=val [attr^=val][attr$=val]