适配器模式—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 ...
随机推荐
- php安全编程—sql注入攻击
php安全编程--sql注入攻击 定义 SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因 ...
- Android项目记录点滴2
1.把本机地址广播出去private void sendIP() { try { DatagramSocket dgSocket = new DatagramSocket(8989); byte[] ...
- (asp.net MVC学习)System.Web.Mvc.HtmlHelper学习及使用
在ASP.NET MVC框架中没有了自己的控件,页面显示完全就回到了写html代码的年代.还好在asp.net mvc框架中也有自带的HtmlHelper和UrlHelper两个帮助类.另外在MvcC ...
- 获取Json对象的长度或计数
最近又开始写博客了.因为最近的工作又开始与技术方面接口了.现在在开发WEB的时候,经常会遇到JSON对象的传递,JSON是个好东西,但是它却没有提供一些简单便捷的处理方法,其中获取JSON对象的长度就 ...
- Criteria 和 DetachedCriteria的区别与使用(转)
转自:http://javapub.iteye.com/blog/1149709 Criteria 和 DetachedCriteria 的主要区别在于创建的形式不一样, Criteria 是在线的, ...
- delphi 实现微信开发
大体思路: 1.用户向服务号发消息,(这里可以是个菜单项,也可以是一个关键词,如:注册会员.) 2.kbmmw web server收到消息,生成一个图文消息给微信,在图文消息中做好自己的url,在u ...
- Android HandlerThread的用法
HandlerThread 继承自Thread,内部封装了Looper. 首先Handler和HandlerThread的主要区别是:Handler与Activity在同一个线程中,HandlerTh ...
- CC++初学者编程教程(10) 搭建Android java C/C++ NDK QTforAndroid 开发环境
1 安装JDK 2 点下一步 3 点下一步 4 开始安装 5 定制路径,点下一步 6 开始安装 7 安装完成, 8 解压缩 9 启动eclipse 10 看到启动画面 11 设置工作文件夹 12 单击 ...
- Android Activity 启动模式详解
最近有群里的朋友问我 Activity的四种启动模式分别是什么意思? 当初因为项目比较忙,草草的解释了下, Api文档中说的也只是一般,在这里就小记一下吧,以便有更多的朋友对Activity启动模式了 ...
- linux安装mongodb并启动
CentOS6.4 安装MongoDB 1.下载MongoDB(64位) http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.9.tg ...