C++|一个小实例理解STL六大部件
STL主要包含容器(containers)、迭代器(iterators)、空间配置器(allocator)、配接器(adapters,或叫适配器)、算法(algorithms)、仿函数(functors,或叫函数对象)等六大部件,相互关连构成一个整体:
我们知道,数组也是一个容器,但它的缺陷就是类型不是泛型,不能动态增长容量,STL的容器就是用来解决此类问题。通过模板来支持泛型,通过allocator(也是模板)来支持动态增长容量,通过迭代器(用类封装指针的模板类,实现了诸如运算符:*、->、++、--、+n、-n的重载来实现指针的移动)来容器和算法,由此实现算法作用在容器上的泛化。
以下是一个有使用到STL六大部件的小实例:
vector<int,allocator<int> > vi(ia,ia+6);
vector是一种容器类型。
int是容器内元素的类型。
allocator是指定的分配器类型,其也是一种模板,需要指定类型,这里指定int。容器构造函数有提供默认的分配器,所以也可以写为:
vector<int> vi(ia,ia+6);
模板类的构造函数有重载,可以使用数组的元素做为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};
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是否定返回值是单目的函数,std中还有not2它是否定返回值是双目的函数。
not2 example:
// not2 example
#include // std::cout
#include // std::not2, std::equal_to
#include // std::mismatch
#include // std::pair
int main () {
int foo[] = {10,20,30,40,50};
int bar[] = {0,15,30,45,60};
std::pair firstmatch,firstmismatch;
firstmismatch = std::mismatch (foo, foo+5, bar, std::equal_to());
firstmatch = std::mismatch (foo, foo+5, bar, std::not2(std::equal_to()));
std::cout << "First mismatch in bar is " << *firstmismatch.second << '\n';
std::cout << "First match in bar is " << *firstmatch.second << '\n';
return 0;
}
/* output
First mismatch in bar is 0
First match in bar is 30
*/
附小实例代码:
#include
#include
#include
#include
using namespace std;
int main()
{
int ia[7] = {26,210,12,47,109,83};
vector > vi(ia,ia+6);// 分配器也是一个模板,需要告诉它每次分配的是什么东西
cout<(),40)));// 统计小于40的否定的元素数量
return 0;
}
视频:
[侯捷]C++ STL 体系结构与内核分析
https://www.bilibili.com/video/av45108908?p=2
-End-