C++ PRIMER 学习笔记 第十一章

Posted on 2022-08-10 10:39  金色的省略号  阅读(18)  评论(0编辑  收藏  举报

  第十一章 泛型算法  

  标准库没有 给容器 添加大量的功能函数,而是选择提供一组算法,这些算法大都不依赖特定的容器类型,是“泛型”的,可作用在不同类型的容器和不同类型的元素上,大多数算法是通过遍历由 两个迭代标记 的一段元素来实现其功能,算法通过迭代器访问元素,这些迭代器标记了要遍历的元素范围,包含头文件  #include <algorithm>  

  11.1 概述

  使用 两个迭代器  和 一个值  调用find函数,检查 两个迭代器实参标记范围内  的每一个元素

#include <vector>
#include <algorithm>
#include <iostream>
//using namespace std;  //vector find cout
using std:: vector; using std:: find; using std:: cout;

int main()
{
/*     std::vector<int > arr = { 1,2,3,4,5,6 };
    //vector<int >::const_iterator it =
    std::vector<int >::iterator it =
        std::find( arr.begin(),arr.end(),3 );
    //*it = 100;
    std::cout << ( it==arr.end() ? "no" : "yes" ); */
    
    vector<int > arr = { 1,2,3,4,5,6 };
    //vector<int >::const_iterator it =
    vector<int >::iterator it =
        find( arr.begin(),arr.end(),3 );
    //*it = 100;
    cout << ( it==arr.end() ? "no" : "yes" );
    
    return 0;
}
View Code

   由于指针的行为与作用 在内置数组的迭代器一样,因此可以使用find来搜索数组

#include <algorithm>
#include <iostream>
using std:: find; using std:: cout;

int main()
{
    int arr[] = { 1,2,3,4,5,6 };
    int* p =
        find( arr,arr+6,3 );
    cout << ( p==arr+6 ? "no" : "yes" );
    return 0;
}
View Code

  每个泛型算法的实现都独立于单独的容器,这些算法还是大而不全的,并且不依赖于容器存储的元素类型,标准算法固有地独立于类型,这种算法只在一点上隐式地依赖元素类型:必须能够对元素做比较运算

  迭代器将算法和容器绑定起来泛型算法用迭代器来解决第一个要求:遍历容器,大多数情况下,每个算法都需要使用两个迭代器来指出该算法操纵的元素范围,第二个迭代器被用作终止遍历的哨兵,元素值得比较,有两种解决方法,默认情况下,find操作要求元素类型定义了相等(==)操作符,算法使用这个操作符比较元素,如果元素类型不支持==操作符,需要一个额外的参数:实现元素比较的函数名字

  这些算法从不使用容器操作,因而其实现与类型无关,元素的所有访问和遍历都通过迭代器实现,算法基于迭代器及其操作实现,而并非基于容器操作算法从不修改基础容器的大小,从不直接添加或删除元素,算法操纵插入器(迭代器),将可能导致在容器中添加元素,但是,算法本身从不这么做

  11.2 初窥算法

  标准库定义了一组泛化的算术算法,包含头文件 #include <numeric>,带有输入范围参数的算法总是使用头两个参数标记该范围,理解算法的最基本方法是了解该算法是否读元素、写元素或对元素重新排序

    11.2.1 只读算法

  许多算法只会读取其输入范围内的元素,而不会写这些元素,find、find_first_of、accumulate算法 就是这样的算法,accumulate 算法在numeric头文件中定义,accumulate的第三个参数是必须的,因为accumulate对将要累加的元素类型一无所知,其返回类型就是其第三个实参的类型,find_first_of 带有两对迭代器,每对迭代器类型必须精确匹配,但不要求两对之间的类型匹配,只要这两个序列的元素可以比较即可,比如第一对的类型是list对象,第二对的类型是vector对象、deque对象,只要这两个序列的元素可以使用相等操作符进行比较即可,返回迭代器,指向第一个匹配的元素,如果找不到匹配元素,则返回第一个范围内的end迭代器

    11.2.2 写容器元素的算法

  一些算法写入元素值,在使用这些算法写元素时要当心,必须确保算法所写的序列至少足以存储要写入的元素写入输入序列的元素:fill算法,带有一对迭代器形参,用于指定要写入的范围,所写的值是它的第三个形参的副本;不检查写入操作的算法:fill_n函数带有的参数包括一个迭代器、一个计数器以及一个值,该函数假定对指定数量的元素做些操作是安全的,不可以在没有元素的空容器上调用fill_n函数,对指定数目的元素做写入运算,或者写到目标迭代器的算法,都不检查目标的大小是否足以存储写入的元素

  back_inserter:确保算法有足够的元素存储输出数据的一种方法是使用插入迭代器,插入迭代器是可以给基础容器添加元素的迭代器,使用back_inserter的程序必须包含iterator头文件,back_inserter函数是迭代器的适配器(参数为容器引用),在试图通过这个迭代器给元素赋值时,赋值运算将调用push_back在容器中添加一个具有指定值的元素

  写入到目标迭代器的算法:向目标迭代器写入未知个数的元素,copy函数,带有三个迭代器参数:头两个指定输入范围,第三个则指向目标序列的一个元素,传递给copy的目标序列必须至少要与输入范围一样大,copy(ilsit.begin(),ilist.end(),back_inserter(ivec) ); 这个效率比较差,更好方法是直接用输入范围作为新构造容器的初始化式:vector<int> ivec(ilist.begin(),ilist.end() );

  replace算法对输入序列做读写操作,将序列中特定的值替换为新的值,replace_copy算法,第三个迭代器实参,指定保存调整后序列的目标位置

vector<int> ivec;
replace_copy( ilist.begin(), ilsit.end(), back_inserter(ivec), 0, 42 );
    11.2.3 对容器元素重新排序的算法

   算法不直接修改容器大小,如果需要添加或删除元素,则必须使用容器操作

  sort排序算法,使用小于操作符比较元素,unique算法“删除”重复元素,并返回一个迭代器,表示无重复的值范围的结束(实际上没有删除任何元素),使用容器操作 erase 删除元素

  stable_sort 和count_if 算法,需要一个配套的实用函数,称为谓词谓词是做某些检测的函数,返回用于条件判断的类型,指出条件是否成立,sort算法按字典次序排序,stable_sort保留相等元素的原始相对位置,调用stable_sort后,对于长度相同的元素,将保留其字典顺序,count_if算法返回使谓词函数返回条件成立的元素个数

  11.3 迭代器回顾

  11.4 泛型算法的结构

  算法具有共同的设计基础,C++提供了两种算法模式:一种模式 由算法所带的形参定义;另一个模式则 通过两种函数 命名和重载的规范 定义

    11.4.1 算法的形参模式

   任何其他的算法分类都含有一组形参规范,大多数算法采用下面四种形式之一:

alg(beg,end,other parms );
alg(beg,end,dest,other parms );
alg(beg,end,beg2,other parms );
alg(beg,end,beg2,end2,other parms );

  其中,alg是算法的名字,beg和end指定算法操作的元素范围,称为算法的“输入范文”,但算法是否使用其他形参取决于它所执行的操作,其他形参:dest、beg2、end2它们是迭代器,还有其他的非迭代器形参,它们是这些算法所特有的 

  带有单个目标迭代器的算法:dest形参是一个迭代器,用于指定存储输出数据的目标对象,算法假定无论需要写入多少个元素都是安全的,如果dest是容器上的迭代器,则算法将输出内容写到容器中已经存在的元素上,更普遍的用法是,将dest与某个插入迭代器或者ostream_iterator绑定在一起;带第二个输入序列的算法:通常将联合两个输入范围的元素来完成计算功能,只带有beg2的算法假定以beg2开始的序列与beg和end标记的序列一样大;

    11.4.2 算法的命名规范

  标准库使用一组相同的命名和重载规范,它们包括两种模式:第一种模式包括测试输入范围内元素的算法,第二种模式则应用于对输入范围内元素重新排序的算法

  区别带有一个值或一个谓词函数参数的算法版本:很多算法通过检查其输入范围内的元素实现其功能,这些算法通常要用到标准关系操作符:==或<,其中的大部分算法会提供第二个版本的函数,允许程序员提供比较或测试函数取代操作符的使用,重新对容器元素排序的算法要使用<操作符,这些算法的第二个重载版本带有一个额外的形参,表示用于元素排序的不同运算, 带有谓词函数形参的算法,其名字带有后缀_if (标准库提供另外名字的版本,而非重载版本,其原因在于这两个版本的算法带有相同数目的形参)

  区别是否实现复制的算法版本:无论算法是否检查它的元素值,都可能重新排列输入范围内的元素,在默认情况下,这些算法将重新排列的元素写回其输入范围,标准库也为这些算法提供另外命名的版本,将元素写到指定的输出目标,此版本的算法在名字上添加了_copy后缀

  11.5 容器的特有算法

  list容器上的迭代器是双向的,而不是随机访问类型,在此容器上不能使用需要随机访问迭代器的算法:包括sort及其相关的算法,与其他顺序容器所支持的操作相比,标准库为list容器定义了更精细的操作集合,使它不必只依赖于泛型操作,list容器特有的操作:merge、remove、reverse、sort、splice、unique,sort(),无参使用元素类型的 < 操作符比较元素,sort(comp),有参(函数),使用参数比较元素类型

  remove和unique的list版本修改了其关联的基础容器:真正删除了指定的元素,merge和splice运算会破坏它们的实参,当实参对象的元素合并到调用merge函数的list对象时,实参对象的元素被移出并删除,与对应的泛型算法不同,list容器特有的操作能添加和删除元素