《C++ Primer》--学习9

文章探讨了C++中的泛型算法,包括只读算法如accumulate和equal,强调了使用cbegin()和cend()的好处。介绍了写容器元素的算法,如拷贝和排序,并讲解了如何通过插入迭代器保证空间。此外,文章还深入到重排元素、定制操作,特别是使用lambda表达式的灵活性,包括捕获列表、返回类型和可变性。最后,提到了iostream_iterator在处理输入输出流中的应用。
摘要由CSDN通过智能技术生成

泛型算法

概述

初始泛型算法

除了少数例外,标准库算法都对一个范围内的元素进行操作。我们将此元素范围称为“输入范围”

只读算法

一些算法只会读取其输入范围内的元素,而从不改变元素

 accumulate 的第三个参数的类型决定了函数中使用哪个加法运算符以及返回值的类型

 算法和元素类型

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

此调用将 v 中每个元素连接到一个 string 上,该 string 的初始值为 空串

 对于只读取而不改变元素的算法,通常最好用 cbegin() 和 cend()

操作两个序列的算法

只读算法  equal ,用于确定两个序列是否保存相同的值,它将第一个序列中的每个元素与第二个序列中的对应元素进行比较,如果所有对应元素都相等,则返回true,否则返回 false。

此算法接受三个迭代器:前两个表示第一个序列中的元素范围,第三个表示第二个序列的首元素

 那些只接受一个单一迭代器来表示第二个序列的算法,都假定第二个序列至少与第一个序列一样长

 写容器元素的算法

一些算法将新值赋予序列中的元素。当我们使用这类算法时,必须注意确保序列原大小至少不小于我们要求算法算法写入的元素数目。算法不会执行容器操作,因此它们自身不可能改变容器的大小

 算法不检查写操作

介绍 back_insert

一种保证算法有足够元素空间来容纳输出数据的方法是使用 插入迭代器 。插入迭代器是一种向容器中添加元素的迭代器。通常,当我们通过一个迭代器向容器元素赋值时,值被赋予迭代器指向的元素。而当我们通过一个插入迭代器赋值时,一个与赋值号右侧值相等的元素被添加到容器中

定义在头文件 iterator 中的一个函数

拷贝算法

拷贝算法是向目的位置迭代器指向的输出序列中的元素写入数据的算法

此算法接受三个迭代器,前两个表示一个输入范围,第三个表示目的序列的其实位置,此算法将输入范围内的元素拷贝到目的序列中

 重排容器元素的算法

消除重复单词

定制操作

sort 算法默认使用元素类型的 < 运算符,但是我们可以重载 < 运算符

向算法传递函数

谓词

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

一元谓词,即接受一个参数,二元谓词,接受两个参数

 会使单词按照长度升序排序

排序算法

 stable_sort 算法,是一种稳定排序算法,维持相等元素原有的顺序

lambda 表达式

我们可以向一个算法传递任何类别的可调用对象。对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它为可调用的。

一个 lambda 表达式具有如下形式:

其中, capture list(捕获列表)是一个 lambda 所在函数中定义的局部变量的列表(通常为空)

return type ,parameter list 和 function body 与任何普通函数一样

lambda 必须使用 尾置返回 来指定返回类型

我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体

 向 lambda 传递参数

lambda 不能有默认参数,因此一个 lambda 调用的实参数目用于与形参数目相等

空捕获列表表示此 lambda 不使用它所在函数中的任何局部变量

使用捕获列表

lambda 只能使用那些明确指定的所在函数的体内的局部变量,一个 lambda 通过将局部变量包含在其捕获列表中来指出将会使用这些变量

 捕获列表只用于局部非 static 变量,lambda 可以直接使用局部 static 变量和它所在函数之外声明的名字

lambda 捕获和返回

当定义一个 lambda 时,编译器生成一个与 lambda 对应的新的(未命名的)类类型。

默认情况下,从 lambda 生成的类都包含一个对应该 lambda 所捕获的变量的数据成员。类似于任何普通的类的数据成员,lambda 的数据成员也在 lambda 对象创建时被初始化

值捕获

与参数不同,被捕获的变量的值是在 lambda 创建时拷贝,而不是调用时拷贝

由于是捕获变量的值是在 lambda 创建时拷贝,因此随后对其修改不会影响到 lambda 内对应的值

引用捕获

 当以引用方式捕获一个变量时,必须保证在 lambda 执行时变量是存在的

隐式捕获

可以让编译器根据 lambda 体内的代码来推断我们要使用哪些变量,为了指示编译器推断捕获列表,应在捕获列表中写一个 & 或 = 。& 告诉编译器采用捕获引用方式, = 则表示采用值捕获方式

当我们混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个 & 或 =

当混合使用隐式捕获和显式捕获时,显式捕获的变量必须使用与隐式捕获不同的方式。

 可变 lambda

默认情况下,对于一个值被拷贝的变量,lambda 不会改变其值。

如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上关键字 mutable 

 指定 lambda 返回类型

默认情况下,如果一个 lambda 体内包含 return 之外的任何语句,则编译器假定此 lambda 返回 void 。

参数绑定

标准库 bind 函数

定义在头文件 functional 中,可以将 bind 函数看作一个通用的适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表

auto newCallable = bind(callable, arg_list)

其中,newCallable 本身是一个可调用对象,arg_list 是一个逗号分隔的参数列表,对应给定的 callable 的参数,即,当我们调用 newCallable 时,newCallable 会调用 callable ,并传递给它 arg_list 中的参数

arg_list 中的参数可能包含形如 _n 的名字,其中 n 是一个整数,这些参数是 占位符,表示 

new_Callable 的参数,它们占据了传递给 new_Callable 的参数的 “位置”,数值 n 表示生成的可调用对象中参数的位置:_1 为 newCallable 的第一个参数,_2 为第二个参数

绑定 check_size 的 sz 参数

使用 placeholders 名字

名字 _n 都定义在一个名为 placeholders 的命名空间里,而这个命名空间本身定义在 std 命名空间中

 using std::placeholders::_1;

 bind 的参数

我们可以用 bind 修正参数的值,更一般的,可以用 bind 绑定给定可调用对象中的参数或重新安排其顺序

用 bind 重排参数顺序

 绑定引用参数

再探迭代器

标准库在头文件 iterator 中还定义了额外几种迭代器:

 

插入迭代器 

插入迭代器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素

 iostream 迭代器

iostream_iterator 读取一个输入流,ostream_iterator 向一个输出流写数据

istream_iterator 操作

 使用算法操作流迭代器

 istream_iterator 允许使用懒惰求值

当我们将一个 istream_iterator 绑定到一个流时,标准库并不保证迭代器立即从流读取数据。具体实现可以推迟从流中读取数据,知到我们使用迭代器时才真正读取

ostream_iterator 操作

我们可以对任何具有输出运算符(<< 运算符)的类型定义 ostream_iterator 

当创建一个 ostream_iterator 时,我们可以提供(可选的)第二参数,它必须是一个c风格的字符串(一个字符串字面量或者一个指向以空字符结尾的字符数组的指针)

必须将 ostream_iterator 绑定到一个指定的流,不允许空的或表示尾后位置的 ostream_iterator

 使用流迭代器处理类类型

我们可以为任何定义了输入运算符( >> )的类型创建 istream_iterator 对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值