adapter(配接器)在STL组件的灵活组合运用上,扮演者转换器的角色。adapter来源于一种适配器模式,其功能是:将一个class接口转换为另一个class的接口,使得原本因接口不兼容而不能合作的classes,可以一起运作。
主要包括仿函数配接器(function adapter)、容器配接器(container adapter)、迭代器配接器(iterator adapter)。
应用于容器
STL提供两个容器stack、queue,其实是一种适配器,它们将deque修饰为另一种容器风貌
stack
stack的底层是deque。
stack封装了所有deque的对外接口,只提供了符合stack原则的几个函数
queue
queue的底层是deque
queue封装了所有deque的对外接口,只提供了符合queue原则的几个函数。
应用于迭代器
STL应用率很多应用于迭代器身上的配接器,包括insert iterator、reverse iterator、iostream iterator。
insert iterator
所谓insert iterator
,可以将一般迭代器的赋值操作转换为插入操作。包括三种:
- 专用于尾插的back_insert_iterator
- 专用于头插的front_insert_iterator
- 可用于任意位置插入的insert_iterator
由于这三个insert iterator
的使用不直观,所以STL提供了三个便利函数:back_iterator()、front_inserter()、inserter()。
int ia[] = { 0,1,2,3,4,5 };
deque<int> id(ia, ia + 6);
copy(ia+1, ia+2, front_inserter(id));
copy(id.begin(), id.end(), outite);
cout << endl;
copy(ia + 3, ia + 4, back_inserter(id));
copy(id.begin(), id.end(), outite);
cout << endl;
deque<int>::iterator ite = find(id.begin(), id.end(), 5);
copy(ia + 0, ia + 3, inserter(id,ite));
copy(id.begin(), id.end(), outite);
cout << endl;
reverse iterator
- 所谓reverse iterator,可以将一般迭代器的行进方向逆转,使得原本的operator++变为–,原本的operator–变为++。
- 如果stl算法接收的不是一般的正常的迭代器,而是这种逆转迭代器,它就会以从尾到头的方向来处理序列中的容器。
ostream_iterator<int> outite(cout, " ");
int ia[] = { 0,1,2,3,4,5 };
deque<int> id(ia, ia + 6);
copy(id.rbegin(), id.rend(), outite);
cout << endl;
iostream iterator
所谓iostream iterator,可以将迭代器绑定到某个iostream对象上:
- 绑定到istream对象上的,叫做istream_iterator,用于输入功能
- 绑定到ostream对象上的,叫做ostream_iterator,用于输出功能
//输出
ostream_iterator<int> outite(cout, " ");
int ia[] = { 0,1,2,3,4,5 };
deque<int> id(ia, ia + 6);
copy(id.begin(), id.end(), outite);
cout << endl;
//file操作
//输入 把一个存有整数(int)的文件ints.dat拷贝到一个list中
std::ifstream dataFile("ints.dat");
std::list<int> data2((std::istream_iterator<int>(dataFile)), std::istream_iterator<int>()); // 正确,注意list构造函数的第一个参数两边的括号
//或者这样
std::istream_iterator<int> dataBegin(dataFile);
std::istream_iterator<int> dataEnd;
std::list<int> data3(dataBegin, dataEnd); // 正确
//输出
std::ofstream outFile("outs.dat");
ostream_iterator<int> outite(outFile, " ");
copy(data3.begin(), data3.end(), outite);
延伸:ostreambuf_iterator效率更高
//输入 把一个文本文件的内容拷贝到一个string对象中
std::ifstream inputFile("outs.dat");
//inputFile.unsetf(std::ios::skipws); // 禁止忽略inputFile中的空格
//std::string fileData((std::istream_iterator<char>(inputFile)), std::istream_iterator<char>()); // 速度慢
std::string fileData2((std::istreambuf_iterator<char>(inputFile)), std::istreambuf_iterator<char>()); // 速度快
// 输出
std::ofstream outputFile("outs1.dat");
std::ostreambuf_iterator<char> outite(outputFile);
copy(fileData2.begin(), fileData2.end(), outite);
应用于仿函数
function adapter是所有适配器中数量最庞大的一个,非常灵活。这些配接操作包括bind、negate(否定)、compose(组合),以及对一般函数或者成员函数的修饰(使其称为一个仿函数)。需要使用的话请引入<functional>
function adapter能够事先对一个函数完成参数的绑定、执行结果的否定以及多方函数的组合
function adapter的作用:通过它们之间的compose、bind、修饰能力,几乎可以无限的创建出各种可能的表达式。
对返回值进行逻辑否定:not1、not2;
对参数进行绑定:bind1st、bind2nd;
用于函数合成:compose1、compose2;
用于函数指针:ptr_fun;
用于成员函数指针:mem_fun、mem_fun_ref;
not
class Widget33 {
public:
bool isCertified() const { return true; }
};
// 如果*pWidget是一个未被验证的Widget33,则删除该指针,并把它置成空
void delAndNullifyUncertified(Widget33*& pWidget)
{
if (!pWidget->isCertified()) {
delete pWidget;
pWidget;
}
}
int test_item_33()
{
std::vector<Widget33*> v;
for (int i = 0; i < 5; ++i) v.push_back(new Widget33);
// 删除那些指向未被验证过的Widget33对象的指针,会资源泄露
v.erase(std::remove_if(v.begin(), v.end(), std::not1(std::mem_fun(&Widget33::isCertified))), v.end());
// 一种可以消除资源泄露的做法
// 将所有指向未被验证的Widget33对象的指针删除并置成空
std::for_each(v.begin(), v.end(), delAndNullifyUncertified);
// 删除v中的空指针,必须将0转换成一个指针,这样C++才能正确推断出remove的第三个参数类型
v.erase(std::remove(v.begin(), v.end(), static_cast<Widget33*>(0)), v.end());
// 使用智能指针可防止资源泄露
std::vector<std::shared_ptr<Widget33>> v2;
for (int i = 0; i < 5; ++i) v2.push_back(std::make_shared<Widget33>());
// 下面语句需要编译器必须能够把智能指针类型std::shared<Widget33>隐式转换为对应的内置指针类型Widget33*才能通过编译
//v2.erase(std::remove_if(v2.begin(), v2.end(), std::not1(std::mem_fun(&Widget33::isCertified))), v2.end());
return 0;
}
bind1st(const Operation& op, const T& x)
bind2nd(const Operation& op, const T& x)
bind1st函数代表这么一个操作: x op value; bind2nd函数代表:value op x。
其中,value 是被应用bind函数的对象。这两个适配器函数都用于将一个二元算子转换成一个一元算子。
我们想知道一个vector中元素值大于100的元素个数,利用bind1st函数和less<int>标准库对象类,就是使得100<element。
#include "stdafx.h"
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> vec;
int a;
while (cin >> a)
vec.push_back(a);
cout << count_if(vec.begin(), vec.end(), bind1st(less<int>(), 100));//100 < element?
cout << endl;
return 0;
}
ptr_fun
int LESS(int arg1, int arg2) {
if (arg1 < arg2)
return 1;
else
return 0;
}
int main()
{
vector<int> v{ 1,8,6,9,3 ,10,11 };
//绑定的是第二个参数,所以结果就是<7,即找比7小的元素个数
auto n = count_if(v.begin(), v.end(), bind2nd(ptr_fun(LESS), 7));
//绑定的是第一个参数,所以结果就是7<,即找比7大的元素个数
auto m = count_if(v.begin(), v.end(), bind1st(ptr_fun(LESS), 7));
cout << n << endl;
cout << m << endl;
system("pause");
return 0;
}
mem_fun及mem_fun_ref
将类的成员函数包装成仿函数使用
mem_fun()是一个适配器(adapter),该函数能将类的成员函数包装成仿函数使用,于是成员函数可以搭配各种泛型算法完成所谓的多态调用。
当容器中存放的是对象实体的时候用mem_fun_ref,当容器中存放的是对象的指针的时候用mem_fun。
class Widget33 {
public:
bool m_isc{ true };
bool isCertified() { return m_isc; }
};
int test_item_33()
{
std::vector<Widget33*> v;
for (int i = 0; i < 5; ++i) v.push_back(new Widget33);
v[2]->m_isc = false;
for_each(v.begin(), v.end(), std::mem_fun(&Widget33::isCertified));
//
std::vector<Widget33> vv(4);
vv[2].m_isc = false;
for_each(vv.begin(), vv.end(), std::mem_fun_ref(&Widget33::isCertified));
vv.erase(std::remove_if(vv.begin(), vv.end(), std::mem_fun_ref(&Widget33::isCertified)),vv.end());
return 0;
}
比如需要找出某个序列中所有不小于12的元素个数.
ostream_iterator<int> outit(cout, " ");
int iaq[] = { 2,21,12,7,19,23 };
vector<int> ivv(iaq, iaq + 6);
//not1 对返回值进行逻辑否定, bind2nd对参数进行绑定
cout << count_if(ivv.begin(), ivv.end(), not1(bind2nd(less<int>(), 12)));
cout << endl;
对仿函数的适配器见:STL源码剖析之仿函数_小飞侠hello的博客-CSDN博客