STL源码剖析之配接器
时间:2023-04-26 12:07:00
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 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::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 outite(cout, " "); int ia[] = { 0,1,2,3,4,5 }; deque 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 outite(cout, " "); int ia[] = { 0,1,2,3,4,5 }; deque id(ia, ia 6); copy(id.begin(), id.end(), outite); cout << endl; //file操作 // 存有一个整数(int)的文件ints.dat拷贝到一个list中 std::ifstream dataFile("ints.dat"); std::list data2((std::istream_iterator(dataFile)), std::istream_iterator()); // 正确,注意list构造函数两侧的括号 //或者这样 std::istream_iterator dataBegin(dataFile); std::istream_iterator dataEnd; std::list data3(dataBegin, dataEnd); // 正确 //输出 std::ofstream outFile("outs.dat"); ostream_iterator 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(inputFile)), std::istream_iterator()); // 速度慢 std::string fileData2((std::istreambuf_iterator(inputFile)), std::istreambuf_iterator()); // 速度快 std::ofstream outputFile("outs1.dat"); std::ostreambuf_iterator outite(outputFile); copy(fileData2.begin(), fileData2.end(), outite);
用于仿函数
function adapter它是所有适配器中最大的,非常灵活。这些配接操作包括bind、negate(否定)、compose(组合),修改一般函数或成员函数(使其称为仿函数)。如需使用,请引入
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 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()); // 一种消除资源泄漏的方法 // 所有未经验证的指向都将被验证Widget删除33个对象的指针并将其置于空中 std::for_each(v.begin(), v.end(), delAndNullifyUncertified); // 删除v中的空指针,必须将0转换成一个指针,这样C 正确推断remove第三参数类型 v.erase(std::remove(v.begin(), v.end(), static_cast(0)), v.end()); // 使用智能指针可以防止资源泄露 std::vector> v2;
for (int i = 0; i < 5; ++i) v2.push_back(std::make_shared());
// 下面语句需要编译器必须能够把智能指针类型std::shared隐式转换为对应的内置指针类型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
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
vector vec;
int a;
while (cin >> a)
vec.push_back(a);
cout << count_if(vec.begin(), vec.end(), bind1st(less(), 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 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 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 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 outit(cout, " ");
int iaq[] = { 2,21,12,7,19,23 };
vector ivv(iaq, iaq + 6);
//not1 对返回值进行逻辑否定, bind2nd对参数进行绑定
cout << count_if(ivv.begin(), ivv.end(), not1(bind2nd(less(), 12)));
cout << endl;
对仿函数的适配器见:STL源码剖析之仿函数_小飞侠hello的博客-CSDN博客