绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
C++|一个小实例理解STL六大部件
2019-12-12 10:04:48

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-

 

分享好友

分享这个小栈给你的朋友们,一起进步吧。

IT知识联盟
创建时间:2019-07-05 15:30:45
分享收集到的大小知识点
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

栈主、嘉宾

查看更多
  • 王超
    栈主

小栈成员

查看更多
  • ?
  • youou
  • gamebus
  • chinacc
戳我,来吐槽~