泛型算法
- 独立于特定的容器
- 和迭代器的联系非常紧密
- 并不直接操作容器
上面三条一定要理解!
从三方面去理解泛型算法:
泛型算法可以大致分为三类:只读、可写、重排。这一小节的用意就是针对每一类泛型算法介绍几个典型的函数,因为每一类算法在结构上都是相似的。
只读
这种算法只会读取给定范围内的元素
accumulate
定义在numeric头文件中,用于求和
int sum = accumulate(vec.begin(), vec.end(), 0);
算法的前两个参数用于表示一个迭代器范围,第3个参数表示求和的初始值。
返回值是迭代器范围内元素的和(以第三个参数为初始值)
第三个参数的类型特别重要,因为算法并不知道元素的类型,所以它会根据第3个参数的类型来推断使用哪种加法。
比如下面的调用就是错误的
string sum = accumulate(v.cbegin(), v.cend(), "");
表面上看第三个参数是一个string,但实际会被看作是一个字符数组,所以第3个参数的类型是const char*,由于const char*没有加法运算,所以调用是错误的
正确的写法是
string sum = accumulate(v.cbegin(), v.ceng(), string(""));
equal
作用于两个序列,它将第一个序列中的元素与第二个序列中的对应元素进行比较。
bool bIsEqual = equal(v1.cbegin(), v1.cend(), v2.cbegin());
它只接受三个参数,并没有指定第二个容器的结尾位置,这就要求第二个容器至少和第一个容器一样长,否则算法会发生错误。
这里并不要求两个容器的元素类型相同,只要能使用比较运算符==就可以了。
比如vector<string>和list<const char*>
操作两个序列的算法还有另外一种形式,就是传递四个参数,分别代码两个迭代器范围。
写算法
更新容器中元素的值。算法不会执行容器操作,因此不会改变容器的大小。
要求我们输入的范围不能比容器的范围大,这一点同样需要程序员自己来保证。
fill和fill_n
将给定的值赋予输入范围内的元素
fill(v.begin(), v.end(), 0);
fill(v.begin(), v.begin() + v.size()/2, 10);
fill_n只接受一个迭代器
fill_n(iter, n, val);
将从iter开始的n个元素赋值为val
要知道泛型算法并不操作容器
vector<int> vec;
fill_n(vec.begin(), 10, 0);
这个调用是错误的,fill_n并不是向容器中插入元素,它只负责更新元素的值
想要在空容器上使用fill_n也是有方法的,需要借助插入迭代器 : back_inserter
vector<int> vec;
auto it = back_inserter(vec);
*it = 42;
back_inserter的参数是容器的引用,返回值是一个特殊的迭代器
vector<int> vec;
fill_n(back_inserter(vec), 10, 0);
这个调用是合法的,因为back_inserter返回的一个特殊的迭代器,它会执行push_back操作。
PS: 推测这个特殊迭代器的递增操作也是在执行push_back
copy
类似于equal,copy也接受三个迭代器,长度同样需要由程序员来保证。
int a1[] = {0,1,2,3};
int a2[sizeof(a1)/sizeof(*a1)];
auto ret = copy(begin(a1), end(a1), a2);
ret指向a2的尾后位置
replace
replace(v.begin(), v.end(), 0, 42);
将指定范围内的所有0替换为42
replace_copy
replace_copy(v1.begin(), v1.end(), back_inserter(v2), 0, 42);
将replace后的容器拷贝一份到v2中
重排元素
sort
将指定范围内的元素重排,它是利用元素类型的<运算符来实现排序的
sort(v.begin(), v.end());
unique
很容易把unique理解为“去重”, 这是不正确的。unique实际也只是执行重排的操作,并不包含“去”的过程(即不会删除元素,算法不会改变容器大小)
auto itr1 = unique(v.begin(), v.end());
unique会重排元素,容器中和其他元素重复的元素会被排到后面。
itr1是一个迭代器,itr1之前的元素都是不重复的,因为unique把和其他元素重复的那些内容挪到了itr1之后
我们可以用erase操作来删除这些元素,这个才是真正意义上的“去重”
v.erase(itr1, v.end());