适配器模式—STL中的适配器模式分析
适配器模式通常用于将一个类的接口转换为客户需要的另外一个接口,通过使用Adapter模式能够使得原本接口不兼容而不能一起工作的类可以一起工作。
这里将通过分析c++的标准模板库(STL)中的适配器来学习adapter模式。由于STL中的适配器过多,不可能全部都具体介绍,所有这里将简单介绍STL中存在的适配器,但将通过具体分析STL中istream_iterator适配器来分析adapter模式。
1. STL中的适配器
在STL中广泛使用了Adapter模式,主要有container adapter、iterator adapter、functor adapter
- container adapter: stack, queue 数据结构
- iterator adapter: front_insert_iterator, back_insert_iterator, istream_iteator, ostream_iterator
- functor adapter: bind, negate, compose 与对一般函数或者成员函数的修饰
2. Container Adapters
在STL中,stack、queue、priority queue是通过借助deque的所提供的接口来实现。
假设客户(希望使用stack、queue、priority queue的程序员)希望能够使用stack数据结构,则需要提供一个能够实现stack功能的类给客户,该类应该提供能够实现stack的push与pop操作的接口给客户调用,并且该类中不应该含有其他与stack能够提供的功能无关的接口。但是,当程序中不存在这样的类时,则可以通过借助已经存在的类deque,定义一个stack类,使用deque所提供的接口来实现stack类应该提供的接口,即上文所述—将一个类的接口转换为客户需要的另外一个接口。
3. Iterator Adapters
迭代器本身是一种模式,迭代器模式使得客户可以抽象地看待各种数据容器,即将所有的数据容器(vector, list, deque等)看做一个数据流。流中的每个元素为一个数据(可以为各种类型的数据),通过对迭代器执行自增与自减操作,可以遍历流中的每一个元素。这样,无论一个数据结构是如何实现的,都可以将其看做一个流,如同数组一样,通过自增自减即可获取下一个元素或者上一个元素。
类似的从抽象角度看待数据容器的观点,使我们想起Unix中将文件、套接字等都统一看做文件来进行处理的观点,文件与套接字都支持从打开、关闭、读取数据、输入数据等操作,但是具体的实现细节不同,对于熟悉面向对象的人而言,能够立即想到面向对象中的多态,接口相同而具体行为不同。
在如今,通过对文件、套接字进一步抽象,将文件、套接字等具有输入与输出功能的物体抽象为输入与输出流,如在Java中的字节流、文件流、套接字等。我们同样可以遍历输入输出流中的每个元素,从这个角度讲,从输入流中读取数据就如同从容器中读取数据一样,向输出流中输入数据则如同向容器中增加元素一样,则在STL中能够适用于容器的算法应该也需要能够使用与输入输出流。istream_iterator与ostream_iterator即是为了能够让适用于容器的算法同样也能够适配于输入输出流而出现的。
void IstreamIteratorTest( )
{
const int size = 10;
auto print = [](int x) { std::cout << x << std::endl; };
std::vector<int> vec;
for (int i = 0; i < size; ++i)
vec.push_back(i);
std::vector<int> copy_from_container(size);
std::copy(vec.begin( ), vec.end( ), copy_from_container.begin());
std::for_each(copy_from_container.begin( ), copy_from_container.end( ), print);
std::vector<int> copy_from_istream(size);
std::istream_iterator<int> int_istream(std::cin), eos;
std::copy(int_istream, eos, copy_from_istream.begin());
std::for_each(copy_from_istream.begin( ), copy_from_istream.end( ), print);
}
在该程序中,copy_from_container拷贝vec容器中的元素,copy_from_istram通过从输入流中读取数据来拷贝到容器中,这里可以将容器vec和输入流都看做可以不断读取数据元素的流,通过将在流中读取的每一个元素都复制到copy_from_container或者copy_from_istream中,也可以看做读取到copy_from_container或者copy_from_istream输出流中,重点是这里使用了相同的算法copy,copy算法本来适用于容器的迭代器,通过对迭代器的自增、自减来遍历获取容器中的元素,而istream与ostream并不支持copy算法中对迭代器的取值与自增、自减等操作,通过使用Adapter模式,使能够使用与容器的算法同样也能够使用与输入与输出流,即—使用Adapter模式能够使得原本接口不兼容而不能一起工作的类可以一起工作。
// copy algorithm
template <typename InputIterator, typename OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result, input_iterator_tag)
{
for (; first != last; ++first, ++result)
*result = *first;
return result;
}
这里给出istream_iterator的实现:
template <typename T, typename Distance> class istream_iterator;
template <typename T, typename Distance>
bool operator==(const istream_iterator<T, Distance>& x, const istream_iterator<T, Distance>& y);
template <typename T, typename Distance = ptrdiff_t>
class istream_iterator {
friend bool operator==<>(const istream_iterator<T>& x, const istream_iterator<T>& y);
public:
typedef input_iterator_tag iterator_category;
typedef T value_type;
typedef const T* pointer;
typedef const T& reference;
typedef Distance difference_type;
istream_iterator( ) : stream(&std::cin), end_marker(false) { }
istream_iterator(std::istream& s) : stream(&s), end_marker(false) { read( ); }
reference operator*( ) const { return value; }
pointer operator->( ) const { return &(operator*( )); }
bool operator!=(const istream_iterator<T, Distance>& rhs)
{
return !(*this == rhs);
}
istream_iterator<T, Distance>& operator++( )
{
read( );
return *this;
}
istream_iterator<T, Distance>& operator++(int)
{
istream_iterator<T, Distance> tmp = *this;
read( );
return tmp;
}
protected:
std::istream* stream;
T value;
bool end_marker;
void read( )
{
end_marker = (*stream) ? true : false;
if (end_marker)
*stream >> value;
end_marker = (*stream) ? true : false;
}
bool equal(const istream_iterator& x) const
{
return (end_marker == x.end_marker) && (!end_marker || stream == x.stream);
}
};
template <typename T, typename Distance>
bool operator==(const istream_iterator<T, Distance>& x, const istream_iterator<T, Distance>& y)
{
return x.equal(y);
}
front_insert_iterator、back_insert_iterator与insert_iterator的出现是因为在某些情况下需要将对迭代器的赋值操作转换为push操作或者insert操作,不同之处在于插入的元素位置不同。如copy算法是将一个迭代器所指的数据拷贝到另一个迭代器所指的位置上,而如果想在该位置插入元素,而并不想对原来的该位置上的元素值进行更改,则需要使用使用插入操作替换赋值操作,这里就可以更加插入的位置不同而选择相应的front_insert_iterator、back_insert_iterator或者insert_iterator。
4. Functor Adapters
仿函数适配器主要用来修饰函数,为函数添加某些需要的功能,在函数式语言如Scheme中可以通过高阶函数实现。在c++中可以通过将每个函数实现为一个class来模拟这种行为。如通过函数g(x) = 3 * x, f(x) = x + 2构造出函数t(x) = f(g(x)),则可以使用Functor Adaptors来实现,下面给出Scheme与c++的实现方法,Java的实现方法与c++类似。
Scheme实现:
(define (func-t func-f func-g)
(lambda (x)
(func-f (func-g x))))
(define (func-f x)
(+ x 2))
(define (func-g x)
(* x 3))
(print ((func-t func-f func-g) 3))
c++实现:
template <typename Func1, typename Func2>
class T {
private:
Func1 func1_;
Func2 func2_;
public:
T(Func1 func1, Func2 func2)
: func1_(func1), func2_(func2)
{ }
int operator()(int x)
{
return func1_(func2_(x));
}
};
void FunctorTest( )
{
auto func1 = [](int x) { return x + 2; };
auto func2 = [](int x) { return x * 3; };
std::cout << T<decltype(func1), decltype(func2)>(func1, func2)(3) << std::endl;
}
适配器模式—STL中的适配器模式分析的更多相关文章
- C++ STL中的常用容器浅谈
STL是C/C++开发中一个非常重要的模板,而其中定义的各种容器也是非常方便我们大家使用.下面,我们就浅谈某些常用的容器.这里我们不涉及容器的基本操作之类,只是要讨论一下各个容器其各自的特点.STL中 ...
- STL之容器适配器queue的实现框架
说明:本文仅供学习交流,转载请标明出处,欢迎转载! 上篇文章STL之容器适配器stack的实现框架已经介绍了STL是怎样借助基础容器实现一种经常使用的数据结构stack (栈),本文介绍下第二种STL ...
- STL中的容器介绍
STL中的容器主要包括序列容器.关联容器.无序关联容器等. 一]序列容器 (1) vector vector 是数组的一种类表示,提供自动管理内存的功能,除非其他类型容器有更好满足程序的要求,否则,我 ...
- stl中顺序性容器,关联容器两者粗略解释
什么是容器 首先,我们必须理解一下什么是容器,在C++ 中容器被定义为:在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对像的指针,这种对象类型就叫做容器.很简单,容器就是保存其它对象的对象 ...
- STL中的优先级队列priority_queue
priority_queue(queue类似)完全以底部容器为根据,再加上二叉堆(大根堆或者小根堆)的实现原理,所以其实现非常简单,缺省情况下priority_queue以vector作为底部容器.另 ...
- 深入解析C++ STL中的常用容器
转载:http://blog.csdn.net/u013443618/article/details/49964299 这里我们不涉及容器的基本操作之类,只是要讨论一下各个容器其各自的特点.STL中的 ...
- STL中的容器
STL中的容器 一. 种类: 标准STL序列容器:vector.string.deque和list. 标准STL关联容器:set.multiset.map和multimap. 非标准序列容器slist ...
- 仿函数(二、stl中常用仿函数)
提到C++ STL,首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法.容器为用户提供了常用的数据结构,算法大多是独立于容器的常用的 ...
- 初步STL该容器适配器
容器适配器 特点 容器一定的顺序来实现(让现有的以集装箱堆放/式工作) 分类 1) stack: 头文件 <stack> • 栈 -- 后进先出 2) queue: 头文件 <que ...
随机推荐
- 点击显示子菜单,离开隐藏子菜单(onmouseout下包含a标签的js解决方法)
<div class="menu"> <a href="javascript:void(0);" id="a_all&quo ...
- Cordova+Asp.net Mvc+GIS
Cordova+Asp.net Mvc+GIS跨平台移动应用开发实战1-系统初步搭建(附演示,apk,全部源码) 1.前言 身处在移动互联网的今天,移动应用开发炙手可热,身为程序猿的我们怎么能错过 ...
- MAC 下使用ipv6、ipv4观看电视、网络电视
换了mac book,ipv6电视却看不了了,为了看电视,只要自己动手了. 关于ipv6是啥和哪里有ipv6这里就不说了,找度娘吧. 我们观看IPv6视频直播信号有两种,一种是mms协议,另外一种 ...
- 初探ListView和Adapter
关于Android Adapter(适配器),参考Devin Zhang’s blog.简单的说,Adapter起到的作用是使得前端的显示和后端的数据能够适配,用以下代码作为例子 1234567891 ...
- 滴滴专车司机苹果手机ios客户端可以下载了
申请过滴滴专车司机的朋友都知道,滴滴专车就是滴滴打车的司机端,但是只有安卓的,一直没有苹果IOS的版本,很多申请通过审核的车主恼于没有IOS版本而暂无法使用滴滴专车司机客户端,也就意味着不能上线接单. ...
- stdin、stdout、stderr
1 ferror 2 stdin 3 stdout 4 stderr 1 ferror 功能:检测文件是否出现错误 返值:未出错0,出错非0 说明:每次调用文件输入输出函数,均产生一个新的ferror ...
- 使用过渡场景在多个场景的切换COCOS2D(4)
CCNode有三个方法,使用CCDirector的replaceScene方法替换场景时,每个节点都会调用这三个方法: onEnter与onExit方法在改变场景过程中的特定时刻被调用,这取决于是否使 ...
- jQuery学习笔记(一)——基础选择器、过滤选择器、表单选择器
$()就是jQuery中的函数,它的功能是获得()中指定的标签元素.如演示样例中$("p")会得到一组P标签元素,当中"p"表示CSS中的标签选择器.$()中的 ...
- 使用jquery-mockjax模拟ajax请求做前台測试
一般来说,我们做web开发的时候前后台是分开做的,这样带来的优点是前台的开发者能够安心的写前台代码而后台的小伙伴就安心研究后台就OK了. 可是这样带来一个问题.当后台的小伙伴被天灾军团带走了,那前台须 ...
- Freedur为什么会免费?
难道没人看看他们的官方站点吗? Freedur倒闭了...... 一个中国人,Chris Lee,作为Freedur的会计师,窃取了公司的银行帐号.并将Freedur的官方站点指向自己的空间.而且声称 ...