c++泛型算法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++泛型算法相关的知识,希望对你有一定的参考价值。
顺序容器只定义了很少的操作,为了能做其他更多有用的操作:查找特定元素,替换或删除某一特定值,重排元素顺序等。泛型算法是一些经典算法的公共接口
1.概述
大多数算法都定义在头文件algorithm中,标准库还在头文件numeric中定义了一组数值泛型算法。
泛型算法不会执行容器的操作,只会运行于迭代器之上,执行迭代器的操作 。这样就导致算法不能改变容器的大小,也就不能直接添加或删除元素了。因此标准库定义了一种叫插入器的特殊迭代器来完成向容器添加元素的效果。
2.初识泛型算法
标准库提供了超过100个算法。
a.只读算法
只读取,如find,count,accumulate,equal。equal算法假定第二个序列至少和第一个序列一样长,只接受三个迭代器,前两个表示第一个序列的范围,第三个是第二个序列的首元素。
b.写容器元素的算法
使用这类算法是,必须注意确保序列原大小至少不小于我们要求算法写入的元素数目。算法fill接受一对迭代器表示一个范围,接受一个值作为第三个参数。
函数fill_n接受一个单迭代器、一个计数值和一个值。但是不能在空容器上调用fill_n,因为向目的位置迭代器写入数据的算法假定目的位置足够大,能容纳要写入的元素。
一种保证算法有足够元素空间来容纳输出数据的方法是使用插入迭代器。使用一般的迭代器想容器元素赋值时,容器内元素的值呗改变。但是使用插入迭代器赋值时,一个与该值相等的元素被添加到容器中。
插入迭代器如back_inserter,此函数返回与容器绑定的插入迭代器,当向插入迭代器赋值是,会调用push_back将一个具有给定值的元素添加到容器中,如以下代码:
vector<int> vec;//空容器
auto it=back_inserter(vec);
*it = 42;//vec中有个元素,值42
拷贝算法:接受三个迭代器,前两个表示一个输入范围,第三个表示目的序列的起始位置。copy算法,该算法返回的是目的位置迭代器的值。replace算法接受四个参数,前两个是迭代器,第三个是要搜索的值,第四个是新值。replace_copy算法保持原序列不变,只是拷贝一份原序列并替代。
c.重排容器元素的算法
sort会重排元素,使之有序。unique会重排输入序列,将相邻的重复项移到最后面,并返回一个指向不重复值范围末尾的迭代器。erase用于删除元素。
3.定制操作
a.比如,sort默认从小到大排序,但是我们的要求可能不同,因此需要重载sort的默认行为。为了按长度重排vector,要使用sort的第二个版本,它接受第三个参数,此参数是一个谓词。
谓词:谓词是一个可调用的表达式,其返回结果是一个能用作条件的值。根据参数个数分为一元谓词和二元谓词
lambda:我们可以向一个算法传递任何类别的可调用对象。到目前为止,使用过的两种可调用对象是函数和函数指针。还有其他两种可调用对象:重载了函数调用运算符的类以及lambda表达式。
一个lambda表达式表示一个可调用的代码单元。可将其理解为一个未命名的内联函数。表达形式如下:
[capture list] (parameter list) -> return type {function body}// capture list(捕获列表)是定义的局部变量的列表,lambda必须使用尾置返回指定返回类型。必须包括捕获列表和函数体。
lambda注:a.不能有默认参数。b.捕获列表捕获它所在函数的局部变量。程序如下:
程序功能:查找第一个长度大于等于sz的元素
auto wc = find_if(words.begin(),words.end(),[sz](const string &a){return a.size()>=sz;})
[=,&a,&b]表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量;捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和它所在函数之外声明的名字,例如cout。
for_each算法接受一个可调用对象,并对输入序列中每个元素调用此对象。
b.lambda捕获和返回
值捕获,由于变量被捕获时是在lambda创建时被拷贝,随后对其修改不会影响到lambda内对应的值。只能用拷贝的值,不能改变。若要改变,用mutable。
引用捕获,使用此方法时对变量的修改会影响到lambda内对应的值。
隐式捕获,[=]采用值捕获,[&]采用引用捕获。
可变lambda:默认情况下,拷贝的变量值,lambda不会改变其值。如果希望改变,就必须在参数列表后加上关键字mutable。
指定lambda返回类型:如果一个lambda体包含return之外的任何语句,则编译器假定此lambda返回void。为了使lambda返回特定类型,就需要使用尾置返回类型->。
c.参数绑定
对于只在一两个地方使用的简单操作,lambda表达式是最有用的。如果要在很多地方使用相同的操作,通常应该定义一个函数。
但是函数一般接受的参数较多,但是一般的算法只接受一元谓词(一个参数的函数),为了解决这个矛盾,我们使用bind的函数。
auto newC=bind(c,list);//将多个参数转化成一个参数。
auto check6 = bind(check_size,_1,6);//向函数check_size绑定两个参数,生成一个可调用对象。为了使用_1,需要声明:using std::placeholders::_1;
bind参数:auto g=bind(f,a,b,_2,c,_1); 这样的话使用g(_1,_2);将会映射为f(a,b,_2,c,_1);
绑定引用参数使用ref函数。
4.再探迭代器
迭代器类型:a.插入迭代器b.流迭代器c.反向迭代器d.移动迭代器。
a.插入迭代器
三种类型:back_inserter//创建一个使用push_back的迭代器 front_inserter//创建一个使用push_front的迭代器 inserter//创建一个使用insert的迭代器
例如:it=inserter(c,iter);//得到插入迭代器it。*it=val;//在it指向的元素前加入val
b.iostream迭代器
两种类型:istream_iterator读取输入流,ostream_iterator向一个输入流写数据。示例程序:
istream_iterator<int> int_iter(cin),eof;//从cin读取int vector<int> vec(in_iter,eof);//构造vec
c.反向迭代器
crbegin()表示容器的尾部。若r为反向迭代器,为得到正常的迭代器,使用r.base()就能得到普通的迭代器。
5.泛型算法结构
(1)迭代器类别:输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机访问迭代器。
a.输入迭代器
find和accumuluate要求输入迭代器,而istream_iterator是一种输入迭代器。
b.输出迭代器
copy的第三个参数是输出迭代器,ostream_iterator是一种输出迭代器。
c.前向迭代器
replace要求前向迭代器,forward_list上的迭代器是前向迭代器。
d.双向迭代器(reverse)
e.随机访问迭代器(sort, array、deque、string、vector的迭代器)
(2)算法形参模式
alg(beg,end,other args);
alg(beg,end,dest,other args);//dest也是迭代器
alg(beg,end,beg2,other args);
alg(beg,end,beg2,end2,other args);
6.特定容器算法
list和forward_list定义了几个成员函数的算法,他们定义了独有的sort,merge,remove,reserse和unique。
链表类型还定义了splice算法。链表的特有版本的算法会改变底层容器,这是与通用版算法的区别。
以上是关于c++泛型算法的主要内容,如果未能解决你的问题,请参考以下文章