C++ Primer笔记(九)泛型算法

1、标准库容器定义的算法很少,而是选择提供一组算法。

泛型是指这些算法不依赖特定的容器类型,可作用在不同类型的容器和不同类型的元素上。不但可以作用在list和vector上还可以作用在内置数组上。

迭代器将算法和容器绑定起来。大多数算法是通过遍历有两个迭代器标记的一段元素来实现其功能。

2、find函数接受两个迭代器和一个值作为参数,它检查两个迭代器实参标记范围内的每一个元素,只要找到与给定值相等的元素,就会返回指向该元素的迭代器,如果没有匹配元素,find就返回它的第二个迭代器实参,表示查找失败。

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


3、标准算法固有地独立于类型,它与容器的类型无关,但是算法只在一点上隐式的依赖于元素类型:必须能够对元素做比较运算。泛型算法用迭代器遍历容器,所有迭代器都支持自增操作符,从一个元素定位到下一个元素,并提供解引用操作符访问元素的值。

大多数情况下,每个算法都需要(至少)两个迭代器来指出该算法操纵的元素范围,第一个迭代器指向第一个元素,第二个迭代器指向最后一个元素的下一个位置,它是被用作终止遍历的哨兵。它也被用作返回值,表示没有找到要查找的元素。

默认情况下,find函数要求元素类型定义了相等操作符,算法使用这个操作符来比较元素,如果元素不支持相等操作符,或者打算用不同的方法来比较元素,可以使用第二个版本的find函数,它需要一个额外的参数:比较函数名。


4、标准库提供超过100中算法,它们都有一致的结构。

泛型算法是基于迭代器及其操作实现,而并非基于容器操作。使用普通的迭代器时,算法不修改容器的大小,它也许会改变存储在容器中元素的值,也许会在容器内移动元素,但是它从不直接添加或删除元素。

使用算法标准库要包含algorithm头文件。

5、find和accumulate算法为只读算法。

它只会读取一段范围的元素,而不会修改这些元素。accumulate带有三个形参,头两个指定要累加的元素范围,第三个则是累加的初值,函数返回计算结果,返回类型是第三个实参的值。accumulate对要累加的类型一无所知,因此该函数必须传递一个起始值,否则accumulate将不知道使用什么起始值,其次,容器内的元素类型必须与第三个实参的类型匹配,或者可转换为第三个实参的类型。


6、使用find_first_of时,带有两对迭代器参数来标记两段元素范围,在第一段范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素。如果找不到匹配元素,则返回第一个范围的end迭代器。两对迭代器类型可以不必相同,如第一对是list容器的迭代器,第二对是vector类型的迭代器。但只要它们指向的元素可以比较就可以。

7、注意:通常泛型算法都是在标记容器(或其他序列)内的元素范围的迭代器上操作的。标记范围的两个实参类型必须精确匹配,而迭代器本身必须标记一个范围:它们必须指向同一个容器中的元素(或者超出容器末端的下一位置),并且如果两者不相等,则第一个迭代器通过不断地自增,必须可以到达第二个迭代器。


8、一些算法会将数据写入输入序列。必须要保证迭代器所标记的范围至少足以存储要写入的元素。另外一些带有一个额外的迭代器参数指定写入目标。这类算法将目标迭代器用作输出的位置。还有将指定数目的元素写入某个序列。

写入到输入序列的算法本质上是安全的:只会写入与指定输入范围数量相同的元素。

如fill函数,带有一对迭代器用于指定写入的范围,第三个形参为要写入的值。而fill_n参数为:一个迭代器,一个计数器,一个值。该函数从迭代器指向的元素开始,架构指定数量的元素设置为给定的值。此时应注意,不要使迭代器越界访问。常见错误是:在没有元素的空容器上调用fill_n函数。

注意:对指定数目的元素做写入运算时,或者写到目标迭代器的算法,都不检查目标的大小是否足以存储要写入的元素。


9、back_inserter,插入迭代器用于给基础容器添加元素的迭代器,通常用普通迭代器时,被赋的值是迭代器所值相等的元素,而使用插入迭代器赋值时,则会在容器添加一个元素。

使用back_inserter必须包含iterator头文件。

back_inserter是迭代器适配器,与容器适配器一样,迭代器适配器是用一个对象作为实参,并生成一个适应其实参行为的新迭代器。

如:vector<int> vec;

back_inserter(vec); //插入迭代器将生成一个绑定到vec对象的插入迭代器,试图使用这个迭代器赋值时,赋值运算将调用push_back在容器中添加一个具有指定值的元素。

如:vector<int>  ivec;

fill_n(back_inserter(vec),10,0);


10、copy函数向目标迭代器写入一定数量的元素。

copy带有三个迭代器参数,头两个指定输入的范围,第三个指向目标序列的一个元素,传递给copy的目标序列必须至少要与输入的范围一样大。

如:vector<int> ivec;

copy (ilist.begin(), ilist.end(), back_inserter(ivec) );

//copy从输入范围中读取元素,然后将它们复制给目标ivec。

11、replace算法带有四个形参:一对迭代器指定输入范围的迭代器和两个值。每一个等于第一个值的元素将被替换为第二个值。

如:replace(ilst.begin(), ilst.end(), 0, 42);//将所有值为0的实例替换成42。


12、sort算法,带有两个迭代器形参指出要排序的范围,使用<操作符比较元素。

13、unique算法,带有两个指定元素范围的迭代器参数,该算法“删除”相邻的重复的元素,然后重新排列。加引号是因为,它是将无重复的元素复制到序列的前段,覆盖了相邻的重复元素,它的返回值为迭代器,它指向超出无重复的元素范围末端的下一位置。

14、标准库定义了四种不同的排序算法,除了sort外还有stable_sort,稳定排序保留了相等元素的相对位置。sort和stable_sort都有两个版本,另一个版本带有第三个形参:比较所使用的函数的名字。这个函数必须接受两个实参,实参的类型必须与元素类型相同,并返回一个用作条件检测的值。


15、C++语言还提供了另外三种迭代器:(以下三种都在iterator头文件中定义)
1)插入迭代器:迭代器适配器,与容器绑定在一起,产生与该容器绑定的插入迭代器,用于实现在容器中插入元素。
2)iostream迭代器:与输入或输出流绑定到一起,用于遍历所关联的IO流。

3)反向迭代器:用于实现从后向前遍历,所有容器类型都有定义了自己的反向迭代器类型。由rbegin和rend返回。


16、插入迭代器,创建一个迭代器,用来给容器添加元素,它是一种迭代器适配器,带有一个容器参数,并生成一个 迭代器,用于在指定容器中插入元素。通过插入迭代器赋值时,迭代器将会插入一个新的元素。

C++提供三种插入器,其差别在于插入的元素位置不同。

back_inserter:创建使用push_back实现插入的迭代器。

front_inserter:使用push_front实现插入。

inserter:使用insert实现插入,它还带有第二个参数:要插入的位置迭代器。


1)front_inserter的操作类似于back_inserter,该函数创建一个迭代器,调用它所关联的基础容器的push_front成员函数代替赋值操作。因此只有当容器提供push_front时,才能使用front_inserter。所以 vector是不可以使用的。

2)inserter将产生在指定位置实现插入的迭代器。inserter函数总是在它的迭代器实参所标明的位置之前插入元素。

3)iostream虽然不是容器,但是标准库同样提供了在iostream对象上使用的迭代器。istream_iterator用于读取输入流,ostream_iterator用于写入到输出流。使用流迭代器时,可以使用泛型算法从流对象中读取数据。


1:istream_iterator in(strm);//创建从输入流strm中读取T类型对象的istream_iterator对象。

2:istream_iterator<T> in;//istream_iterator对象的超出末端迭代器。

3:ostream_iterator<T> in(strm);//创建将T类型的对象写到输出流strm的ostream_iterator对象。

4:ostream_iterator<T> in(strm,delim);//创建将T类型的对象写到输出流strm的ostream_iterator对象,在写入的过程中使用delim作为元素的分隔符。delim是以空字符结束的字符数组。


17、流迭代器只定义了最基本的迭代器操作:自增、解引用和赋值。可以比较两个istream_iterator,但是ostream迭代器则不提供比较运算。

在创建流迭代器时,必须指定迭代器所读写的对象类型。


18、流迭代器都是类模板:任何已定义输入操作符(>>)的类型都可以定义istream_iterator。类似的任何已定义输出操作符的类型也可以定义ostream_iterator。

如:istream_iterator<int> cin_it(cin);//从cin读取int类型的对象。

ostream_iterator对象必须与特定的流绑定在一起。在创建istream_iterator时,可直接绑定到一个流上。

另一种方法是在创建时不提供实参,该迭代器指向超出末端位置。

如:istream_iterator<in>end_of_stream;

但是ostream_iterator不提供超出末端迭代器。


19、构造与流绑定在一起的istream_iterator对象时,将对迭代器定位,以便第一次对该迭代器进行解引用时即可从流中读取第一个值。

如:istream_iterator<int> in_iter(cin);

istream_iterator<int>eof;

while(in_iter!=eof)

    vec.push_back(*in_iter++);

该程序也可以这样写:

istream_iterator<int>in_iter(cin);

istream_iterator<int>eof;

vector<int>vec(in_iter,eof);

可以使用ostream_iterator对象将一个值写入到写入流中。其操作过程与使用迭代器将一组值逐个赋给容器中的元素相同。

如:ostream_iterator<string> out_iter(cout,"\n");

istream_iterator<string> in_iter(cin),eof;

while(in_iter!=eof)

   *out_iter++=*in_iter++;


20、反向迭代器是一种反向遍历容器的迭代器。

它从最后一个元素向第一个元素遍历容器,它还将自增(减)的含义反过来:对于反向迭代器,++运算将访问前一个元素,而--运算将访问下一个元素。容器定义了rbegin和rend,分别返回指向容器尾元素和首元素前一位置的反向迭代器。反向迭代器也有常量和非常量类型

例如:为了降序排列vector可以向sort传递一对反向迭代器。

sort(vec.rbegin(), vec.rend());

注意:流迭代器不能创建反向迭代器。


21、反向迭代器调用成员函数base()将获得与反向迭代器指向相同元素的正向迭代器。

注意:反向迭代器用于表示的范围是不对称的,所以可知使用普通的迭代器对反向迭代器进行初始化或赋值时,所得到的迭代器并不是指向原迭代器所指向的元素。

22、算法要求用于指定范围的两个迭代器具有完全一样的类型。调用m.end()返回的类型,依赖于m的类型。如果m为const对象则返回const_iterator类型,否则返回普通的迭代器。


23、算法要求的迭代器分为五个类别,分别为:

1)输入迭代器:读,不能写,只支持自增运算。只能顺序使用,一旦自增就不能检查之前的元素。

2)输出迭代器:写,不能读,只支持自增运算。对于指定的迭代器应该使用一次*运算,而且只能使用一次。

3)前向迭代器:读和写,只支持自增运算。用于读写指定的容器,这个迭代器只会以一个方向遍历序列。它支持对一个元素的多次读写。

4)双向迭代器:读和写,支持自增和自减运算。从两个方向读写容器。所有标准库提供的迭代器都至少达到双向迭代器的要求。

5)随机访问迭代器:读和写,支持完整的迭代器算术运算。提供在常量的时间内访问容器任意位置的功能。


23、需要随机访问迭代器的泛型算法包括sort算法,vector,deque,string迭代器都是随机访问迭代器。用作访问数组元素的指针也是随机访问迭代器。

除了输出迭代器外,其他类别的迭代器形成了一个层次结构:需要低级类别迭代器的地方,可以使用任意一种更高级的迭代器。对于需要输入迭代器的算法,可传递向前、双向或随机访问迭代器。而需要随机访问迭代器时,必须传递随机访问迭代器。


24、map,set和list类型提供双向迭代器。而string,vector和deque容器上定义的迭代器是随机访问迭代器,用作内置数组元素的指针也是随机访问迭代器。istream_iterator是输入迭代器,ostream_iterator为输出迭代器

注意:尽管map,set类型提供双向迭代器,但关联容器只能使用算法的一个子集,这是因为:关联容器的键是onst对象。因此关联容器不能使用任何写序列元素的算法。

25、对于list对象,应该优先使用list容器特有的成员版本,而不是泛型算法。其特有的操作能添加和删除元素。

向算法传递无效的迭代器类别所引起的错误,无法保证会在编译时被捕获到。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值