C++ 小白 学习记录10

泛型算法

因容器的方法比较少, 为了扩展容器的方法, 标准库搞了一套算法,来实现排序/查找等高级功能, 因为是容器通用的所以称为泛型.

一般通过两个迭代器指定的元素范围来操作. 多数定义在头文件algorithm中.

算法可能会改变容器内元素的值或者移动容器内的元素, 但是不会改变容器的大小,即不会添加或删除元素

只读算法, 如find, count, accumulate(求和, numeric头文件中), equal

accumulate 在操作string时, 原书中将不可以使用字符串的字面值常量, 14中可以使用,但是结果不符合预期.

accumulate(v.cbegin(), v.cend(), string(" "));

accumulate(v.cbegin(),v.cend(), " "); // c++ 14 未报错, 但结果不如何预期

equal(first.begin(), first.end(), secord.begin()) // 判断first和second包含的元素是否相等

只接受一个单一迭代器来表示第二个序列的算法, 都假定第二个序列至少与第一个序列一样长.由此引申, equal以第一个序列循环.

  • 如果第二个序列短于第一个, 则运行时报严重错误.
  • 即使是第二个比第一个长,比第一个多的部分也不会参与比较.

写容器的算法: fill, replace

fill(v.begin(),v.end(),value)

fill_n(b.begin, n, value)  不能写空序列, 只能是修改,否则 错误

replace(v.begin(), v.end(), valueForSearch, newValue)

replace_copy(v.begin(), v.end(), back_insert(ivec), 0, 42)  替换后的序列保存到ivec中

介绍back_inserter  插入迭代器 在iterator头文件中, 用于插入元素

拷贝算法 copy

copy(v.begin(), v.end(), target.begin()) 返回copy后目的位置迭代器的值

重排容器元素算法 sort, unique

sort(v.begin(), v.end())

auto ret = unique(v.begin(), v.end()) // 返回一个指向不重复值 范围末尾的迭代器

定制操作 -- 自定义比较方法

谓词: 一个可调用的表达式, 其返回结果是一个能用作条件的值.

一元谓词: 只接受单一参数

二元谓词: 接收两个参数

向算法传递函数

排序算法

void elimDups(vector<string>& words) {
	sort(words.begin(), words.end());
	auto end_unique = unique(words.begin(), words.end());
	words.erase(end_unique, words.end());
}
bool isShorter(const string& s1, const string& s2) { return s1.size() < s2.size(); }

elimDups(words); // 将words按照字典重排, 并消除重复单词

stable_sort(words.begin(), words.end(), isShorter); // 按照长度重排, 长度相同的单词维持字典序

lambda表达式 理解为一个未命名的内联函数,

与函数不同的地方是:

  • 可以定义在函数内部
  • 必须使用尾置返回来指定返回类型.
  • 参数不能有默认值

[capture list](parameter list) -> return type {function body}

capture list 捕获列表, lambda所在函数中定义的局部变量

参数列表和返回类型可忽略, 捕获列表和函数体必须得有

lambda 

值捕获: 赋值, 拷贝

引用捕获 赋值 引用 [&v1]

隐式捕获 自己推断, [=] 值捕获方式,  [&] 引用捕获方式

混合方式 [&, parm], 当使用混合模式时, 捕获列表中第一个元素必须是一个&或者=

如果lambda中不是单一的return, 如果不指定返回值类型则默认为void返回类型.

int v1 =10;

auto f=[v1]() mutable {return ++v1;}

mutable 不可以改变v1的值, 其返回值是v1+1的副本, 并非v1 真正+1了, 如果不使用mutable, 则++v1, 会编译失败.

bind函数 (functional)                                                                                                                                          auto newCallable = bind(callable, arg_list)

当调用newCallable时, newCallable会调用callable 并传递arg_list中的参数

auto check6 = bind(check_size,  std::placeholders::_1, 6);

std::placeholders::_1 参数占位符

bind 函数 可以 改变目标函数的参数 个数, 顺序

当bind需要传递一个引用类型的参数时 需要使用ref/cref函数

再探迭代器

  • 插入迭代器: 用于容器插入元素 *it, ++it, it++ 不会做任何事情
  • 流迭代器: 输入输出流上,可以遍历关联的io流
  • 反向迭代器 除了forward_list 之外都有反向迭代器, 向后移动而不是向前移动.
  • 移动迭代器 只为移动容器中的元素.
	vector<int> vec = { 1,2,3,4,5,6,7,8,9 };
	ostream_iterator<int> out_iter(cout, " d ");
	for (auto e : vec) *out_iter++ = e;
	cout << endl;
	copy(vec.begin(), vec.end(), out_iter); // copy 也可以输

反向迭代器会导致string的反向输出, 需要使用反向迭代器中的base()方法转换为普通迭代器.

	string s = "First, middle, last";
	auto rcomma = find(s.crbegin(), s.crend(), ',');
	cout << string(s.crbegin(), rcomma) << endl; // 输出tsal
	cout << string(rcomma.base(), s.cend()) << endl; // 输出last
	cout << string(rcomma, s.cend()) << endl; // 编译错误

上面代码中的rcomma 和recomma.base() 指向的并不是一个位置

泛型算法结构

  • 输入迭代器                只读不写, 单遍扫描, 只能递增
  • 输出迭代器                只写不读, 单遍扫描, 只能递增
  • 前向迭代器                可读写, 多遍扫描, 只能递增
  • 双向迭代器                可读写, 多遍扫描, 可递增递减
  • 随机访问迭代器        可读写, 多遍扫描, 支持全部迭代器运算

输入迭代器 如find, accumulate 要求输入迭代器. istream_iterator 是输入迭代器:

  • 支持 == !=
  • 支持++
  • 支持 * 解引用
  • 箭头运算符 ->  等价于(*it).member

输出迭代器 如copy的第三个参数, ostream_iterator 是输出迭代器:

  • 支持 ++
  • 支持 *, 只出现在赋值运算符的左侧
  • 只能向一个输出迭代器赋值一次

前向迭代器 replace要求前向迭代器 , forward_list 上的迭代器是前向迭代器

双向迭代器 除了forward_list之外, 其余的都能提供双向迭代器. reverse要求双向迭代器.

随机访问迭代器

  • 支持 < <= > >=
  • 支持 迭代器与整数 +, +=, -, -=
  • 支持 两个迭代器上的减法运算符
  • 支持 下标运算符

高级别迭代器支持低层类别迭代器的所有操作

array, deque, string, vector 都是随机访问迭代器

算法的形参模式

  • alg(beg, end, other args);
  • alg(beg, end, dest, other args);
  • alg(beg, end, beg2, other args);
  • alg(beg, end, beg2, end2, other args);

beg, end,表示操作范围,  dest 一般表示可以写入的目的位置的迭代器. beg2, end2 表示第二个输入范围. 如果只有beg2, 则beg2的范围至少比beg, end指定的范围大.

算法命名规范

一般都会接受一个第三个参数作为谓词用于替换<, ==.

_if  谓词版本的函数.

_copy, 一般算法的操作都是直接作用的第一对迭代器代表的范围上, 而_copy版本 则会返回一个新的容器.

特定容器的算法

链表类型: list forward_list

  • lst.merge(lst2)                lst2 抽取lst1, 抽取后lst2 变空
  • lst.merge(lst2, comp)
  • lst.remove(val)                调用erase删除与给定值相等的每个元素
  • lst.remove_if(pred)        上述的谓词版本
  • lst.reverse()                     反转
  • lst.sort()                            升序排序
  • lst.sort(comp)
  • lst.unique()                       去重
  • lst.unique(pred)

链表特有的操作会改变容器. 如通用版本的merge, 不会改变参数中的容器, 而链表版本的merge会销毁参数中容器.

splice 实现list拼接的功能。将源list的内容部分或全部元素删除,拼插入到目的list

  • void splice ( iterator position, list<T,Allocator>& x );
  • void splice ( iterator position, list<T,Allocator>& x, iterator it );
  • void splice ( iterator position, list<T,Allocator>& x, iterator first, iterator last );

position 是要操作的list对象的迭代器

list&x 被剪的对象

对于一:会在position后把list&x所有的元素到剪接到要操作的list对象
对于二:只会把it的值剪接到要操作的list对象中
对于三:把first 到 last 剪接到要操作的list对象中

	list<int> a = { 1,2,3 };
	list<int> b = { 4,5,6 };
	// a.splice(a.end(), b);  // a: 123456
	// a.splice(a.end(), b, (++b.begin())); // a: 1235
	a.splice(a.end(), b, ++b.begin(), b.end()); // a: 12356
	ostream_iterator<int> out_iter(cout," ");
	copy(a.cbegin(), a.cend(), out_iter);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yutao1131

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值