什么是STL?
STL是“Standard Template Library”,简单来说就是标准工具库,是C++编译器提供的一套易用的现成的算法工具。
优点
如何OI选手能够合理运用STL,可以节省大量代码量,快速切题。 实际上,Ol选手常说的STL除了指真正属于STL中的函数以外,还包含一些易用的库函数。由于STL内容实在不多,在这里我们讲一点其他常用函数、使用技巧。都是用法、不太动脑子,内容不多,所以我可能会插两道要用STL的题让大家想一想。
STL六大部件
STL主要包含容器(containers)、迭代器(iterators)、空间配置器(allocator)、配接器(adapters,或叫适配器)、算法(algorithms)、仿函数(functors,或叫函数对象)等六大部件,相互关连构成一个整体: 数组也是一个容器,但它的缺陷就是类型不是泛型,不能动态增长容量,STL的容器就是用来解决此类问题。通过模板来支持泛型,通过allocator(也是模板)来支持动态增长容量,通过迭代器(用类封装指针的模板类,实现了诸如运算符:*、->、++、--、+n、-n的重载来实现指针的移动)来容器和算法,由此实现算法作用在容器上的泛化。
以下是一个有使用到STL六大部件的小实例:
vector<int,allocator
vector是一种容器类型。
int是容器内元素的类型。
allocator是指定的分配器类型,其也是一种模板,需要指定类型,这里指定int。容器构造函数有提供默认的分配器,所以也可以写为:
vector
模板类的构造函数有重载,可以使用数组的元素做为vector容器的元素。
适配器在STL中扮演着转换器的角色,本质上是一种设计模式,用于将一种接口转换成另一种接口(或者说连接不同的接口),从而是原本不兼容的接口能够很好地一起运作。比如我们有一个接口 void fun(CString a) , 只接受字符串类型变量, 但是提供的数据又只有一个整型变量int a, 这时就需要一个适配器来把 int a 转换 CString a;
适配器不提供迭代器。
根据目标接口的类型,适配器可分为以下几类:
(1) 改变容器的接口,称为容器适配器; (2) 改变迭代器的接口,称为迭代器适配器; (3) 改变仿函数的接口,称为仿函数适配器。
STL提供了序列式容器,同时针对序列式容器提供了应用于不同场景的容器适配器,通俗讲容器适配器就是以序列式容器为底层数据结构,进一步封装了的为适应场景应用的容器。STL中提供了三种适配器,分别为stack,queue和priority_queue。
函数对象的适配器,用于特化和扩展一元或者二元函数对象,主要分为两类:
绑定器(binder):给二元函数对象绑定一个常量,转化成一元函数对象。包括bind1st和bind2nd。
取反器(negator):将谓词函数对象结果取反。包括not1和not2。
对于迭代器适配器,如输入流和输出流都不支持迭代器,而STL算法都是通过操作迭代器来实现的,如果需要用STL算法处理流中输入的数据时,就用std::istream_iterator,这就是一个适配器,它提供输入迭代器的接口,使用istream来实现。迭代器适配器包括
三种reverse(逆向)适配器,如rbegin(),rend()等。
insert迭代器,带有一个容器参数,有back_inserter(容器)、front_inserter(容器)、inserter(容器,位置)。
stream适配器:如ostream_iterator,istream_iterator。
bind1st和bind2nd函数用于将一个二元函数对象(binary functor,bf)转换成一元函数对象(unary functor,uf)。为了达到这个目的,它们需要两个参数:要转换的bf和一个值(v)。
我们在做比较的时候所写的表达式像 x > k ,x < k,这里的k是一个参数,表示你程序里面的表达式要和k值去比较。bind2nd简单的理解就是把k作为比较表达式的第二个参数。如果使用bind1st则对应的表达式是 k > x,k < x,也就是把k作为比较表达式的第一个参数。
f = std::bind1st( functor, v); 'f( x)'等价于'functor( v, x)'
f = std::bind2nd( functor, v); 'f( x)'等价于'functor( x, v)'
bind1st和bind2nd例子:
int a[] = {1, 2, 100, 200}; std::vector< int> arr(a, a + 4); // 移除所有小于100的元素 arr.erase( std::remove_if( arr.begin(), arr.end(),std::bind2nd( std::less< int>(), 100)), arr.end()); 这里的比较表达式相当于arr.value < 100。
如果用bind1st则表达的意思就恰恰相反:
// 移除所有大于100的元素 arr.erase( std::remove_if( arr.begin(), arr.end(),std::bind1st( std::less< int>(), 100)), arr.end()); 这里的表达式相当于100 < arr.value。
为了实现删除大于100的元素你同样可以使用bind2nd:
// 移除所有大于100的元素 arr.erase( std::remove_if( arr.begin(), arr.end(),std::bind2nd( std::greater< int>(), 100)), arr.end()); x <= k怎么实现呢,std又提供了一个好东西not1,我们可以说 !(x > k) 和 x <= k是等价的,那么我们看看下面的表达式:
// 移除所有小于等于100的元素 arr.erase( std::remove_if( arr.begin(), arr.end(),std::not1(std::bind2nd( std::greater< int>(), 100))), arr.end()); not1是构造一个与谓词结果相反的一元函数对象,not2是构造一个与谓词结果相反的二元函数对象。 int a[] = {1, 2, 100, 200};
vector< int> arr(a, a + 4);
// 移除所有小于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
bind2nd( std::less< int>(), 100)), arr.end());
//这里的比较表达式相当于arr.value < 100
//如果用bind1st则表达的意思就恰恰相反
// 移除所有大于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
bind1st( std::less< int>(), 100)), arr.end());
//这里的表达式相当于100 < arr.value
//当然为了实现删除大于100的元素你同样可以使用bind2nd
// 移除所有大于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
bind2nd( std::greater< int>(), 100)), arr.end());
//前面说道=的比较,比如说x <= k怎么实现呢,std又提供了一个好东西not1,
//我们可以说 !(x > k) 和 x <= k是等价的,那么我们看看下面的表达式:
// 移除所有小于等于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
not1(std::bind2nd( std::greater< int>(), 100))), arr.end());
//说明:not1是否定返回值是单目的函数,std中还有not2它是否定返回值是双目的函数。```cpp int a[] = {1, 2, 100, 200};
vector< int> arr(a, a + 4);
// 移除所有小于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
bind2nd( std::less< int>(), 100)), arr.end());
//这里的比较表达式相当于arr.value < 100
//如果用bind1st则表达的意思就恰恰相反
// 移除所有大于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
bind1st( std::less< int>(), 100)), arr.end());
//这里的表达式相当于100 < arr.value
//当然为了实现删除大于100的元素你同样可以使用bind2nd
// 移除所有大于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
bind2nd( std::greater< int>(), 100)), arr.end());
//前面说道=的比较,比如说x <= k怎么实现呢,std又提供了一个好东西not1,
//我们可以说 !(x > k) 和 x <= k是等价的,那么我们看看下面的表达式:
// 移除所有小于等于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
not1(std::bind2nd( std::greater< int>(), 100))), arr.end());
//说明:not1是否定返回值是单目的函数,std中还有not2它是否定返回值是双目的函数。
not2 example:
```cpp
#include <bits/stdc++.h>
using namespace std;
int main () {
int foo[] = {10,20,30,40,50};
int bar[] = {0,15,30,45,60};
pair<int*,int*> firstmatch,firstmismatch;
firstmismatch = mismatch (foo, foo+5, bar, equal_to<int>());
firstmatch = mismatch (foo, foo+5, bar, not2(equal_to<int>()));
cout << "First mismatch in bar is " << *firstmismatch.second << '\n';
cout << "First match in bar is " << *firstmatch.second << '\n';
return 0;
}
/* 输出结果:
First mismatch in bar is 0
First match in bar is 30
*/
附小实例代码:
/*#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
*/
#include <bits/stdc++.h>
using namespace std;
int main()
{
int ia[7] = {26,210,12,47,109,83};
vector<int,allocator<int> > vi(ia,ia+6); // 分配器也是一个模板,需要告诉它每次分配的是什么东西
cout<<count_if(vi.begin(), vi.end(), not1(bind2nd(less<int>(),40)));// 统计小于40的否定的元素数量
return 0;
}