C++:泛型算法基础

来源:互联网 发布:无尽之剑3宝石数据修改 编辑:程序博客网 时间:2024/05/01 19:02

泛型算法

顺序容器只定义了很少的操作,我们希望容器支持更多的操作。C++标准库为了满足更多开发者的需求,实现了一些经典算法的公共接口,因为这些算法不依赖于具体的容器,而是借助迭代器对容器进行操作,所以常称其为泛型算法

泛型算法都定义在<algorithm>头文件中,少数数值型算法定义在<numeric>头文件中。

一般情况下,算法不会直接操作容器,而是借助容器的迭代器来遍历、操作容器中的元素。而且,大多数算法绝对不会改变容器的长度。(erase和insert等操作不是算法,而是“容器操作”,注意分清)

算法分类

只读算法

只读算法会读取输入范围内的元素,而绝对不会改变元素,这种情况下我们一般使用常量迭代器。

  • find:
auto it = find(intVec.cbegin(), intVec.cend(), value);if (it != intVec.cend())    cout << *it << endl;

find查找容器中是否存在值为value的元素,如果不存在返回尾后迭代器。

  • accumulate:
auto sum = accumulate(intVec.cbegin(), intVec.cend(), 0);

accumulate定义在<numeric>头文件中,用来求容器中一段范围的和,第三个参数是sum的初始值,注意初始值的选取和容器中元素的类型是有关系的。如果是double型要初始化为0.0.
值得一提的是,accumulate并不支持所有的元素类型。
元素类型必须定义了“+”操作才可以用accumulate。
例如:

vector<char*> vec ={ "i am" , "you are"};auto sum = accumulate(vec.cbegin(),vec.cend()," ");

因为char*并不支持‘+’操作,所以编译器报错,如果是vector<string>是可以的。

  • equal:
    equal算法用于确定两个序列是否保存相同的值。
equal(c1.cbegin(), c1.cend(), c2.cbegin());

该算法接收三个迭代器。前两个迭代器表示第一个序列的范围,第三个迭代器是第二个序列的起始迭代器位置。这个算法基于这样的编程假设:

1.c1和c2容器类型不必相同,但是容器中的元素类型必须相同
2.容器中的元素类型必须支持“==”操作。(例如某些自定义类可能没有重载“==”运算符,那么就不能用该算法)
3.第二个序列的长度至少和第一个序列的长度相同

写操作算法与迭代器适配器

前边曾经提到过,绝大部分算法不会修改容器的长度,这好像限制了某些操作。
例如:

  • fill_n:
    fill_n算法原型fill_n(dest,n,val)向目标位置dest填充n个值为val的元素,dest是一个迭代器。
vector<int> intVec;//空容器fill_n(intVec.begin(), 10, 0);

这里运行会报错。因为规定算法不可以修改容器的长度,此时容器长度为0。这一点很容易出现致命错误,因为编译器不会检查算法的写操作是否合法,需要程序员保证
我们可以先分配给intVec一些空间后,再指向fill_n操作。

intVec.resize(20);fill_n(intVec.begin(), 10, 0);

注意这里是resize而不是reserve。vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector的capacity同时也增加了它的size!

除了刚才的方法,还可以用一种叫做”插入迭代器”的方式来得到我们期望的结果。
插入迭代器back_inserter是一种”迭代器适配器“。

适配器是一种接口转换器,它可以让一种东西看起来像另一种东西。

在这里back_inserter使得迭代器能够像函数push_back进行一样的操作,它使得一个迭代器看起来像是调用了push_back一样。

fill_n(back_inserter(intVec), 10, 0);//添加10个0到intVec中去

这里在提醒一下,我们之前说过算法不可以改变容器的size,那这里为什么可以成功运行不出错呢?
原因在于,算法操作了一个迭代器,迭代器可以完成向容器中添加元素的功能,但是算法本身永远不会这么做,所以和我们之前说的并不矛盾。

auto it1 = back_inserter(intVec);//插入器绑定到了intVec上并且生成了一个迭代器it*it1 = 10;//相当于intVec.push_back(10)

除了back_inserter之外还有front_inserter(调用push_front)和inserter调用容器操作的inserter

例如:auto it3 = inserter(intVec, intVec.begin())在intVec.begin()位置之前调用insert()容器操作。

当然,vector<typename>并不可以使用front_inserter(),因为vector<typename>上没有定义push_front操作。

  • copy:
    copy向一个目标位置写入一定范围内的数据。
    这个算法接受三个迭代器copy(it1,it2,dest)表示将[it1,it2)范围内的元素copy到dest为起始位置的序列(dest是一个迭代器).
    由于copy是算法不会改变目标容器的长度,所以下述操作是错误的:
vector<int> l;copy(intVec.cbegin(), intVec.cend(), l.begin());

l是空容器,l.size() == 0,copy不可以改变其size所以操作出错。可以预先改变l的size或者利用back_inserter
改变l的size:

vector<int> l;l.resize(100);copy(intVec.cbegin(), intVec.cend(), l.begin());

利用back_inserter

vector<int> l;copy(intVec.cbegin(), intVec.cend(), back_inserter(l));
  • replace:
    replace操作替换某一段区间的值replace(it1,it2,value,aim_value)将[it1,it2)区间内的value值替换为aim_value
replace(l.begin(), l.end(), 0, 101);

将l的所有0替换为101.

1 0