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提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...
随机推荐
- Python开发基础-Day9-生成器、三元表达式、列表生成式、生成器表达式
生成器 生成器函数:函数体内包含有yield关键字,该函数执行的结果是生成器,生成器在本质上就是迭代器. def foo(): print('first------>') yield 1 pri ...
- Xamarin Android SDK无法更新的解决办法
Xamarin Android SDK无法更新的解决办法 Xamarin Android SDK无法更新的解决办法,更新时候,提示警告信息:A folder failed to be moved. ...
- 【BZOJ 2118】 2118: 墨墨的等式 (最短路)
2118: 墨墨的等式 Description 墨墨突然对等式很感兴趣,他正在研究a1x1+a2y2+…+anxn=B存在非负整数解的条件,他要求你编写一个程序,给定N.{an}.以及B的取值范围,求 ...
- BM算法--串匹配
BM(Boyer-Moore)算法,后缀匹配,是指模式串的比较从右到左,模式串的移动也是从左到右的匹配过程,一般情况比KMP算法要快.时间复杂度O(m/n) C++描述(教师版) int BM(cha ...
- [转]详解spring 每个jar的作用
spring.jar 是包含有完整发布模块的单个jar 包.但是不包括mock.jar, aspects.jar, spring-portlet.jar, and spring-hibernate2. ...
- 判断IE版本的HTML语句详解,如:<!--[if IE 9]> 仅IE9可识别 <![endif]-->
我们常常会在网页的HTML里面看到形如[if lte IE 9]……[endif]的代码,表示的是限定某些浏览器版本才能执行的语句,那么这些判断语句的规则是什么呢?请看下文: 注意:以下用法不支持IE ...
- winform WebBrowser如何修改使用默认的IE浏览器版本
在搜了一些相关资料原来WebBrowser使用的是IE的兼容模式进行浏览(IE7模式). 建议:先添加注册表中,然后使用注册表编辑器导出功能,产生reg注册文件:方便日后使用. Winform We ...
- rocketmq持久化方式
推荐看下RocketMQ,使用文件做持久化, 并支持分布式事务(虽然可能造成较多的写脏), 异步刷盘,内存预分配, 高可用采用了同步双写及异步复制的方式, 通信是用netty做的,基本上所有耗时的操作 ...
- Ubuntu下Wine使用教程
转自:http://blog.csdn.net/wangchangshuai0010/article/details/12057251 用了段时间的Ubuntu,感觉很好!可是工作的时候还是要用到 w ...
- [Android Pro] Property Animation
声明:下面的内容需要Android API level 11的支持 Property Animation是如何运作的 首先,来看一下两个不一样的Property Animation场景: 场景一(Li ...