这篇文章的内容是泛型算法。前面的一章讲的是各种各样的方法,那些方法有些是针对容器的,有些是针对迭代器的。本章主要介绍的是针对迭代器的,这些方法死记硬背总不太好吧,那么这章将会比较深刻地分析其背后规律帮助我们更好地理解记忆那些方法。
然后,学习本章感觉得结合设计模式的思想来理解会更有意思,要不然有点枯燥。
要开始进阶了。泛型 --一个能提高程序复用度的利器。
这段话可以理解为一个通用的算法。
以上就是在讲find函数如何使用的。
1)算法如何工作
2) 迭代器算法不依赖于容器
即对不同的迭代器find函数都可以使用。
3)算法依赖于元素类型的操作
这些概述大致是在将泛型算法具有哪些特点。
第一句话很重要:泛型算法运行于迭代器之上,执行迭代器的操作。
1)算法和元素类型
泛型算法对容器是通用的,但是需要考虑容器中的元素类型。 通过accumulate方法引出这一规律。
2)操作两个序列的算法
通过举例equal方法来引出如何用迭代器操作两个不同的容器。
1)算法不检查写操作
2)back_inserter
这种迭代器可以拿来批量化为容器添加元素。
3)拷贝算法
copy方法的使用方式与前面介绍的equal方法类似。
个人感觉这些方法都不太实用,真的遇到了还不如自己写一个来的快。
这是引入,后面的几个小内容都会以这个举例。
1)消除重复单词
2)使用unique
注意最后一句话:标准库算法对迭代器而不是容器进行操作。
3)使用容器操作删除元素
再次注意!:迭代器是无法真正删除或添加元素的,要添加和删除必须是容器操作
还是不太明白为什么讲泛型算法讲着讲着就引入lambda表达式了,这个内容引入设置感觉不太友好。
首先要清楚什么是谓词,本书没介绍什么是谓词就给出这个概念,对新人也太不友好了吧。
谓词函数就是一个返回布尔值的函数。可参考:
C++谓词函数(无师自通)
C++ - 算法(algorithm) 的 谓词(predicate) 详解_Mystra-CSDN博客_c++谓词
1)谓词
举例说明:
bool special(const int& a, const int& b) {
return a > b;
}
void Learn9GenericAlgorithm::specialOperation()
{
vector vec{1, 9, 3, 7, 2, 9, 8, 7, 6, 6, 5};
sort(vec.begin(), vec.end(), special);//greater()
for (const int& v : vec)cout << v << " ";//从小到大输出
}
2)排序算法
这一块跳跃有点快,有点难理解,可以建议先看后面的例子再返回来理解这一大段话。
1)介绍lambda
关于lambda的使用可参考:C++之Lambda表达式 - 季末的天堂 - 博客园
2)向lambda传递参数
3)使用捕获列表
这一块理解起来相对简单。
4)调用find_if
理解到这一步再返回去看前面介绍的那一大段话就理解多了。
5)for_each算法
for_each方法可以方便简洁地对容器的遍历。
7)完整的biggies函数
即将上面讲的都串起来了。
1)值捕获
类似函数的值传递
2)引用捕获
与函数的引用参数传递类似。不能返回局部变量的引用值得注意。
注意事项:
3)隐式捕获
上面讲了那么多不如直接看下面这个表理解的快。
4)可变lambda
fcn3()和fcn4()这两个函数看懂了大致就知道什么意思了。即非引用捕获要在lambda函数体内改变捕获值那么要加上mutable。
举个例子:
int lam = 2;
auto r = [&lam] {return ++lam; };
auto r1 = [lam]()mutable {return ++lam; };//()mutable一个都不能少写
lam = 0;
cout << r() <
5)指定lambda返回类型
以上情况编译器可以自动推断返回类型,因此可以省略尾置返回类型。
而这个例子我们就需要使用尾置返回类型了,否则编译器无法推断返回类型。
1)标准库bind函数
有点类似与lambda表达式的一个替代方案。
2)绑定check_size的sz参数
以下内容即举例说明bind函数怎么用
3)使用placeholder名字
感觉讲了一堆废话,即使用占位符要引入命名空间placeholder
4)bind的参数
5)使用bind重排参数顺序
是对bind函数的一个灵活运用。
6)绑定引用参数
接下来要介绍这几种迭代器了。
关于插入迭代器可参考:插入迭代器_lusic01的专栏-CSDN博客_插入迭代器
主要区分上面三个插入迭代器之间的区别。
list lst = { 1,2,3 }, lst1,lst2;
auto iter1 = front_inserter(lst1);
copy(lst.cbegin(), lst.cend(), iter1);
copy(lst.cbegin(), lst.cend(), back_inserter(lst2));
for (auto const& l : lst1)cout << l << " ";//输出321
for (auto const& l : lst2)cout << l << " ";//输出123
1)istream_iterator操作
讲了那么多不如跑跑代码理解理解:
istream_iterator int_iter(cin),eof;
while (int_iter != eof)cout << *(int_iter++) << " ";
2)使用算法操作流迭代器
3)istream_iterator允许懒惰求值
4)ostream_iterator操作
讲的那么多不如自己动手写一写:
ostream_iterator out_iter(cout, " ");
vector vec{ 1,2,3,4,5 };
for (int v : vec)out_iter = v;
cout << endl;
copy(vec.begin(), vec.end(), out_iter);
cout << endl;
5)使用流迭代器处理类类型
反向迭代器没啥好说的,就是顺序是反的迭代器。
1)反向迭代器需要递减运算符
2)反向迭代器与其他迭代器之间的关系
1)迭代器类别什么
为甚有一堆迭代器类别,主要是为了使程序的功能进一步解耦以及与细化明确。
可以把迭代器当成指针一样。注意还是有很多迭代器不是随机访问迭代器的,比如set和list等等。
vector vec{ 1,2,3 };
auto iter = vec.begin();
auto* p = &vec.front();
cout << iter[1] << endl << *(iter+1)<
对迭代器算法常见的模式进行总结。
1)接受单个目标迭代器的方法
2)接受第二个输入序列的算法
这个没啥好讲的,就是说这种格式是把一块东西搞到另一块地方。
1) 使用重载形式传递一个谓词的方法
那么谓词重载又该怎么定义函数呢? 这或许得去看看STL的源码了。
2)_if版本的算法
3)区分拷贝元素的版本和不拷贝元素的版本
记住拷贝元素的版本加了个copy即可。
list与forward_list的相关方法经常与其他容器不一样。
1)splice成员
2)链表特有的操作会改变容器