2021-07-25

C++primer第五版第10章笔记

泛型算法

10.1概述

C++将大多数泛型算法保存于头文件algorithm中,并且保留了一部分数值运算算法在头文件numeric中。由于我们一般使用一个范围和值作为算法的公共接口,因此我们可以在支持迭代器或指针的容器或者内置类型数组使用这些泛型算法,从而并不关心存储元素的容器。
如:

find(num.begin(),num.end(),val);(如果查找成功则返回所找值所在迭代器的位置,若失败,则返回第二个参数的迭代器)
int number[]={1,2,3};
find(begin(number),end(number),val);(此时我们使用指针传递范围,因此查找成功返回的是一个int的指针,失败返回第二个参数指针)

10.2初识泛型算法

10.2.1只只读元素是指只读取输入范围内的元素不改变元素值,一般有两个表示范围的参数。
如:find,accumulate(返回值取决于第三个参数),count,equal(因为比较的是迭代器所指元素值,所以可以比较不同容器值,而且元素类型只要可以用==也可以比较,但如果都是const char*需要用strcmp比较)。
10.2.2写容器元素的算法
使用需要写入容器值的算法时,要确保容器能够容纳,因为算法本身并不会改变容器大小。
如:

fill(num.begin(),num.end(),2);将num的元素值全部改写为2
fill_n(num.begin(),num.size(),2);(与上述功能相同,第二个参数接受需要改变元素数量)

若我们无法确保算法操作是否超限,可使用back_inserter(接受容器的引用,将元素插入,并返回插入迭代器)
如:

auto it=back_inserter(val);
*it=42;(向一个存储int类型的容器添加42)
还可与其他算法复用以此确保其他写算法不会超限
fill_n(back_inserter(val),10,0);(向val添加100)
复制算法copy接收三个参数,前两个表示需要拷贝内容的范围,第二个表示拷贝的目的地
copy(num1.begin(),num1.end(),num2.begin());(将num1复制到num2,并且返回指向拷贝完最后一个元素之后的迭代器)
replace(num1.begin(),num1.end(),0,42)(将所有0改为42)
replace_copy(num1.begin(),num1.end(),back_inserter(val),0,42)(第三个参数接收一个迭代器,表示将num中0替换到42后将参数一和二的范围从第三个迭代器位置开始拷贝,此例中使用back_inserter是为了直接将拷贝插入防止空间大小不够)

10.2.3重排容器元素的算法
我们可以使用sort重排容器元素位置,使用的时<比较;可以使用uique来使元素唯一不唯一的部分将放置到容器最后,并且会返回一个指向第一个不唯一位置的迭代器

10.3定制操作

许多算法使用了元素类型<、==操作符来比较元素,我们可以重载这些算法的默认行为来达到我们的目的。
10.3.1向算法传递函数
如果我们想要用sort函数依照长度来进行排列,可以对sort函数添加第三个谓词参数。
谓词一般为一元谓词和二元谓词(接收参数的不同),元素类型必须能转化为谓词参数类型,因为我们需要调用谓词函数,其返回结果为一个条件(bool型),我们在外定义谓词函数的话,在sort中直接添加函数名即可。
如果我们想在长度相等情况下再按字典序排序,可以在谓词中添加条件||,或者采用stable_sort,此方法先按照谓词排序,在谓词排序相等时用<排序。
函数partition可以将容器重新划分,满足谓词的排列前半部分,并且返回一个指向最后满足条件的值之后的迭代器。
10.3.2lambda表达式
有时我们在使用某个算法时,此算法规定了他所能能接收谓词参数的个数,这时如果我们想传入更多参数来实现我们的目的,我们使用可调用对象:函数体、函数指针、重载函数调用符的类、lambda表达式。
lambda表达式相当于没有名字的内联函数,书写时使用尾置返回类型(也可省略,根据函数体来判断)。
如:

[捕获参数列表](参数列表)->return type{函数体}(因为我们是在算法中调用lambda表达式,所以我们只能传入算法规定的参数,额外的参数需要靠捕获列表来获取,注意static和函数之外的局部变量不用捕获能直接用)
auto f=[]{return 42};(捕获列表与函数体部分不能省略,调用直接f()可返回42);
find_if(参数1,参数2,可调用对象);(返回指向满足条件的第一个元素位置的迭代器,只能接收一元谓词)
find_each(参数1,参数2,可调用对象);(对参数12规定的范围的元素值都调用对象)

迭代器相减可获取长度
10.3.3lambda捕获和返回
我们可以采用值或者引用传值的方式来向lambda传递捕获序列,如果我们想将拷贝到lambda的临时拷贝值改变,需要在参数列表后添加mutable。
传递值分为显示与隐式的传值,显示许将每个都写出来;隐式可以使用&或者=表示引用或者值传递,注意如果我们隐式采用了&显示需要采用=,相反一样。
lambda如果只有单一返回语句则可以自动推断,否则为void型,若需返回需要我们使用尾置类型表面。
如:

[]()->int{};
count_if(参数一,参数二,谓词)(返回统计范围类满足谓词的元素个数,此算法接收一元谓词)

10.3.4参数绑定
当我们需要反复使用一个lambda表达式时,可以将其编写为函数再使用bind来返回一个可调用对象。
bind头文件为functional,bind一般将lambda捕获的部分将定值传输,将其传参列表部分用占位符_1表示
如:

auto check6=bind(函数体,_1,sz);(check6接收一个参数,并通过_1传给函数体第一个参数,函数体第二个参数采用定值sz)
check6("hello");(将hello通过check6传递给函数体,同时check6本身也是一个可调用对象)
采用bind函数替换lambda表达式
auto wc=find_if(word.begin(),word.end(),[sz](){});
auto wc=find_if(word.begin(),word.end(),bind(函数体,_1,sz));(find调用可调用对象并传入string,_1接收传入的第一个参数并按位置给函数体传参)
注意我们使用_1需要将使用它所在的命名空间
using namespace std::placeholders;
如果我们有时需要传入引用对象,可以在引用对象上加ref()以此来进行拷贝并传入

10.4再探迭代器

除了容器的迭代器外,C++还在iterator中定义了别的迭代器。
如:插入迭代器、流迭代器、反向迭代器、移动迭代器。
10.4.1插入迭代器
插入器为迭代器适配器,接收一个容器,返回一个插入迭代器向容器中插入元素。
总共有三种类型:back_inserter、front_inserter、inserter(此函数不仅要接收容器,还要接收一个迭代器参数,将元素插入迭代器之前)。
10.4.2iostream迭代器
标准库中定义了IO类型对象的迭代器,保存于头文件iterator,分为istream_iterator、ostream_iterator。

istream_iterator<int> init(cin);(将init绑定到流cin)
istream_iterator<int> end;(尾后迭代器)
while(init!=end){
  num.push_back(*init++);(将cin流的内容依次拷贝到num中,知道遇见别的字符)
}
vector<int>num(init,end);(可使用这种方法直接初始化)
并且我们可以使用某些算法使用io迭代器
count(init,end,0);
ostream_iterator<int>iter(cout," ");(值通过迭代器传入cout,并且每个都会打印空格,第二个参数必须为C字符或者C字符串风格,迭代器无尾后迭代器)
for(auto m:num){
  *iter++=m;(将每个m传入cout流,*++可以省略,但是因为书写一致,一般加上)
}
unique_copy(参数一,参数二,参数三);(将参数范围内元素不重复地传递给参数三)

10.4.3反向迭代器
反向迭代器就是从从尾元素开始向首元素,除forward_list外都支持反向迭代器,我们可以调用base函数将其变为正向迭代器。

10.5泛型算法结构

迭代器按照读、写、随机访问能力可分为5类。
10.5.1 5类迭代器
输入迭代器:可读取序列中的元素。
输出迭代器:只写而不读元素。
前向迭代器:可以读写元素,只能向序列的一个方向移动。
双向迭代器:在前向的基础上还支持–操作。
随机访问迭代器:提供在常量时间内访问任意元素的能力。
10.5.2算法形参模式

alg(beg,end,dest,other algs);(一般算法接收一个范围,dest为其他容器的迭代器,如果指向其他容器,则直接写到其他容器元素,一般为插入迭代器)
alg(beg,end,beg2,other algs);(beg2为第二个迭代器起始位置,一般没有end2默认范围大于等于第一个范围)

10.5.3算法命名规范
某些算法通过重载形式,传递一个谓词代替元素值来实现功能。

unique(beg,end);
unique(beg,end,comp);(此函数名一样,但是却可以通过comp来比较元素,而不是用==)

某些算法会添加_if来实现接收参数一样但功能不同,或者添加_copy来表示将处理好的元素序列不放到输入序列中而是别的指定的位置,或者_copy_if实现上述两种功能。

find(beg,end,val);(查找第一个元素值为val的位置)
find_if(beg,end,pred);(查找第一个令pred为真的位置)
reverse(beg,end);(将反转的序列元素写到输入序列中)
reverse_copy(beg,end,dest)(元素逆序拷贝到dest中)

10.6特定容器算法

由于list和forward_list特殊的存储方式,所以只提供双向和单向迭代器,因此我们为其定值了很多特定算法,使其效率高于调用通用版本的效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值