STL源码剖析之配接器

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博客

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值