C++ 学习笔记(10)泛型算法、lambda表达式、bind函数、迭代器

C++ 学习笔记(10)泛型算法、lambda表达式、bind函数、迭代器

参考书籍:《C++ Primer 5th》
API:算法库lambda表达式std::bind迭代器


10.1 概述

  • 算法永远不不会改变底层容器的大小。

10.2 初识泛型算法

10.2.1 只读算法

  • accumulate,积累。第三个参数类型决定了函数中使用哪个加法运算符以及返回值类型。序列中元素必须与第三个参数匹配。
std::vector<int> v{ 1, 2, 3, 4 };
std::vector<string> vs{ "i", "am","eveything" };

// 累加 1+2+3+4 = 10
int sum = accumulate(v.begin(), v.end(), 0);
// 累成 1*2*3*4 = 24
int product = accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
// 连接字符串 输出 -i-am-everything
string str = accumulate(vs.begin(), vs.end(), string(""), [](string a,string b) { return a + "-" + b; });
  • 那些只接受一个单一迭代器表示第二个序列的算法,都假定第二个序列至少与第一个序列一样长。

10.2.2 写容器元素的算法

  • 如果第二个序列是第一个序列的子集,则程序会产生严重错误:equal会试图访问第二个序列列表末尾之后的元素。

10.2.3 重排容器元素的算法

  • unique将所有重复的元素放到列表后面,返回指向第一个重复元素的迭代器。

在这里插入图片描述


10.3 定制操作

10.3.1 向算法传递函数

  • stable_sort:稳定排序,可以维持相等元素的原有顺序。

  • lambda表达式:未命名的内联函数。[捕获列表] (参数列表) -> 返回类型 { 函数体 }

    • 可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体。
    • 不能有默认参数。
    • 捕获列表只用于非static变量。
    • 可以直接使用static变量和所在函数之外声明的名字。
    • 捕获变量,可以是值或引用。
    • 隐式捕获:&引用方式捕获所有局部变量, =值的方式捕获。

在这里插入图片描述

  • 可以用mutable改变一个被捕获的变量的值。
int a = 4;
auto f = [a] () mutable { return ++a; };		// 不加mutable将无法修改a
cout << f();		// 输出5
cout << a;		// 输出4

10.3.4 参数绑定

  • std::bind:通用的函数适配器。auto newCallable = bind(callable, arg_list);

    • newCallable就是一个可调用对象。
    • arg_list,callable的参数列表。
  • placeholders命名空间:如占位符_1,实际是std::placeholders::_1

using namespace std::placeholders;

void f(int a, int b, int c) { cout << a << " " << b << " " << c << endl; }

auto g = bind(f, 666, _2, _1);
g(10,20);		// 实际调用 f(666, 20, 10);

  • bind参数不能传递引用,可以使用标准库的ref函数。
bind(print, ref(os));		// os是ostream对象,输出流不能拷贝,所以用引用。

10.4 再探迭代器

迭代器 (Iterator)

  • 插入迭代器(insert iterator):绑定到容器,可以向迭代器插入元素。
  • 流迭代器(stream iterator):绑定到输入输出流,可以遍历IO流。
  • 反向迭代器(reverse iterator):往后移动的迭代器,forward_list没有这个迭代器。
  • 移动迭代器(move iterator):用于移动对象。

10.4.1 插入迭代器

  • back_inserter:使用push_back的迭代器。
  • front_inserter:使用push_front的迭代器。
  • inserter:使用insert的迭代器,第二个参数为指定迭代器位置。

在这里插入图片描述

std::vector<int> v{1, 2, 3, 4};
std::fill_n(std::back_inserter(v), 3, -1);		// 在列表后面插入三个-1
for (int n : v)
    std::cout << n << ' ';		// 输出:1 2 3 4 -1 -1 -1

auto it = std::inserter(v,v.begin());		// 前向迭代器。下面几种的输入都一样(*it、++it、it++)。
it = 66;
*it = 22;
++it = 44;
it++ = 55;
for (int n : v)
	std::cout << n << ' ';		// 输出:66 22 44 55 1 2 3 4 -1 -1 -1

10.4.2 iostream迭代器

  • istream_itertor:读取输入流。标准库不保证迭代器立即从流中读数据,但可以保证使用前,已经完成读取流的数据。
    • 创建时绑定到流中,作为输入。
    • 创建时使用默认初始化,则可以当作尾后值。
  • ostream_itertor:向输出流写数据。
    • 必须绑定到流中,不允许空的或表示尾后位置的ostream_iterator。

在这里插入图片描述
在这里插入图片描述

istream_iterator<int> in(cin), eof;
cout << accumulate(in, eof, 0) << endl;		// 输出结果为:计算输入的整形的和,直到文件末端或错误字符。

std::vector<int> v{ 1, 2, 3, 4 };
ostream_iterator<int> out(cout, " ");	// 每次写入后加一个空格=
for (auto e : v)						// 将列表的所有数值直接写入cout。
	out = e;
cout << endl;

10.4.3 反向迭代器

在这里插入图片描述

  • 反向迭代器需要递减运算符。

10.5 泛型算法结构

在这里插入图片描述

10.5.1 5类迭代器

  • 输入迭代器要求:
    • 用于比较两个迭代器的相等和不相等运算符(==、!=)
    • 用于推进迭代器的前置和后置递增运算(++)
    • 用读取元素的解引用运算符(*);只出现在右侧
    • 箭头运算符(->),等价 (*it).member,即解引用迭代器
  • 输出迭代器要求:
    • 用于推进迭代器的前置和后置递增运算(++)
    • 解引用运算符(*),只出现在左侧
  • 前向迭代器要求:
    • ==, !=, ++, *, ->
  • 双向迭代器要求:
    • ==, !=, ++, --, *, ->
  • 随机访问迭代器要求:
    • 用于比较两个迭代器相对位置的运算符(<、<=、>、>=)
    • 迭代器和整数值的加减运算(+、+=、-、-=),计算结果是迭代器位置的移动。
    • 用于两个迭代器上的减法运算符(-),得到两个迭代器的距离
    • 下标运算符(iter[n]),与*(iter[n])等价

10.6 特定容器算法

  • list 和 forward_list,应该优先使用成员函数版本的算法,而不是通用算法。

在这里插入图片描述
在这里插入图片描述

  • 链表版本的算法与通用的区别:会改变底层的容器结构,如会删除一些元素。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值