C++ primer超详细讲解泛型算法

目录
  • 初识泛型算法
    • 只读算法
    • 写容器算法
  • 定制操作
    • lambda表达式
    • lambda捕获和返回
  • 再探迭代器
    • 插入迭代器
    • iostream迭代器
    • 反向迭代器

初识泛型算法

只读算法

只读取输入范围内的函数,不改变元素,find,accumula也是如此

(1)accumulate算法为求和算法,前两个参数指出求和元素范围,第三个是和的初值,例:

int sum=accumulate(v.begin(),v.end(),0)

(2)操作两个序列的算法

equal算法,确定两个序列是否保存相同的值,将第一个序列的每个元素和第二个序列中的每个元素进行比较,若相同返回true,否则返回false,接受三个参数,前两个表示第一个序列的元素范围,第三个表示第二个序列的首元素

equal(r1.begin(),r1.end(),r2.begin())

写容器算法

(1)拷贝算法

向另一个目的位置迭代器指向的输出序列中的元素写入数据算法。此算法接受三个迭代器,前两个表示一个舒服范围,第三个表示目的序列的起始位置。copy返回目的迭代器的值。

int a1[] = { 0,1,2,3,4,5 };
int a2[sizeof(a1) / sizeof(*a1)];
auto ret = copy(begin(a1), end(a1), a2);

定制操作

lambda表达式

(1)定义

一个lambda表达式表示一个可调用的代码单元,可理解为未命名的内联函数

lambda表达式形式:

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

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

auto f =[] {return 42}

调用: cout<<f()<<endl;

(2)向lambda传递参数

实参被用来初始化lambda的形参,lambda不能有默认参数

例:

//[](const string &a,const string &b){return a.size()<b.size()}
//调用
sort(w.begin(),w.end(),[](const string &a,const string &b){return a.size()<b.size()})

(3)使用捕获列表

一个lambda通过将局部变量包含在其捕获列表中指出将会使用这些变量,捕获列表指引lambda在其内部包含访问局部变量所需的全部信息

例如,找出第一个大于等于给定长度的单词。

函数biggies实现

void biggies(vector<string> &words,vector<string>::size_type sz)
{
    auto wc=find_if(words.begin(),words.end(),
                    [sz](const string &a){return a.size()>=sz});
}

lambda捕获和返回

(1)值捕获

与传值参数类似,采用值捕获的前提是变量可以拷贝,与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝

void fcun()
{
    size_t v1=42;
    auto f=[v1]{return v1;};
    v1=0;
    auto j=f();
}

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

(2)引用捕获

void fcun()
{
    size_t v1=42;
    auto f=[&v1]{return v1;};
    v1=0;
    auto j=f();   //j为0,f2保存v1的引用,而非拷贝
}

(3)隐式捕获

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

wc=find_if(w.begin(),w.end(),[=](const string &s){return s.size>=sz;});

(4)可变lambda

在值拷贝的情况下,lambda不会改变其值,若希望能够改变被捕获的变量的值,则在参数列表首加上mutable关键字。

void func()
{
    size_t v1=42;
    auto f=[vi]() mutable{return ++v1;};
    v1=0;
    auto j=f();
}

一个引用捕获的变量能否可以修改依赖于此引用指向的是一个const还是非const类型

void fc()
{
	const size_t v1 = 42;
	auto f = [&v1]() mutable {return ++v1; };//无法修改v1
	auto j = f();
	cout << j << endl;
}

(5)指定lambda的返回类型

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

例如:将一个序列中负数替换为其绝对值

transform(v.begin(),v.end(),v.begin(),[](int i){return i<0?-i:i;})

transform接受三个参数,前两个表示迭代器输入序列,第三个表示迭代器目的位置。

上述中,我们无需指定返回类型,因此可以根据条件运算符的类型推断出来。

若改写成存在if语句,则存在错误

transform(v.begin(),v.end(),v.begin(),[](int i){if(i<0) return -i; else return i;})

编译器发现存在return之外的语句,所以推断其返回类型为void,而他却返回了一个int

正确写法:

transform(v.begin(),v.end(),v.begin(),
                [](int i) ->int {if(i<0) return -i; else return i;})

再探迭代器

标准库头文件iterator中还定义了额外几种迭代器,包括如下:

  • 插入迭代器:绑定到一个容器上,向容器插入元素
  • 流迭代器:绑定到输入输出流上,可以用来遍历所关联的IO流
  • 反向迭代器:这些迭代器向后而不是向前移动,forwar_list不存在此迭代器
  • 移动迭代器:移动元素专用

插入迭代器

例:

list<int> lst = { 1,2,3,4 };
list<int> lst2, lst3;
copy(lst.begin(), lst.end(), front_inserter(lst2));
copy(lst.begin(), lst.end(), inserter(lst3, lst3.begin()));

front_inserter总是插入容器的第一个元素之前,所以lst2中的元素顺序为4,3,2,1,而lst3中的元素顺序为1,2,3,4

iostream迭代器

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

(1)istream_iterator操作

创建流迭代器时,必须指定迭代器将要读写的对象类型,可以创建空的初始化迭代器,用作尾后值使用的迭代器,一旦关联的流遇到文件尾或者IO错误,迭代器的值就与尾后迭代器相等。

例:读取输入数据保存到vector中

vector<int> v;
istream_iterator<int> it(cin);
istream_iterator<int> e;
while (it != e)
    v.push_back(*it++);   //或 v.push_back(it,e)

(2)使用算法操作流迭代器

例:

istream_iterator<int> it(cin);
istream_iterator<int> e;
cout << accumulate(it, e, 0) << endl;

(3)ostream_iterator操作

创建一个ostream_iterator时,可以提供第二参数,他是一个字符串,在输出每个元素后都会打印此字符串,不允许空的或表示尾后的ostream_iterator

vector<int> v = { 1,2,3,4 };
ostream_iterator<int> out(cout, "@@");
for (auto e : v)
	*out++ = e;  //赋值语句实际上是将元素写到cout,且*和++并不做任何事
cout << endl;

反向迭代器

反向迭代器就是在容器中尾元素向首元素反向移动的迭代器,递增一个反向迭代器会向前移动一个元素,递减一个迭代器会向后移动一个元素

将反向迭代器转换为普通迭代器可使用reverse_iterator中的base成员函数来完成转换

例:输出最后一个单词

string line = { "first,middle,last" };
auto r = find(line.rbegin(), line.rend(), ',');
cout << string(r.base(), line.end()) << endl;

到此这篇关于C++ primer超详细讲解泛型算法的文章就介绍到这了,更多相关C++ primer泛型算法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解C++泛型装饰器

    目录 c++ 装饰器 对输出的解释 总结 c++ 装饰器 本文简单写了个 c++ 装饰器,主要使用的是c++ lamda 表达式,结合完美转发技巧,在一定程度上提升性能 #define FieldSetter(name, type, field) \ type field; \ name() {} \ name(const type& field): field(field) { \ cout << "[左值 " << field << &q

  • C++泛型编程基本概念详解

    目录 1.什么是泛型编程? 2.函数模板 (1)函数模板概念 (2)函数模板格式 (3)函数模板的原理 (4)函数模板的实例化 (5)模板参数的匹配原则 3.类模板 (1)类模板的定义格式 (2)类模板的实例化 总结 1.什么是泛型编程? 比如说,我们如何实现一个通用的交换函数呢?int型.double型.char型的交换 void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } vo

  • C++算法与泛型算法(algorithm、numeric)

    本文包括的算法有: 只读算法:find().count().accumulate().equal() 写算法:fill().fill_n().back_inserter().copy().copy_backward().replace().replace_copy().next_permutation().prev_permutation() 重排元素算法:sort().stable_sort().unique() 一.算法简介 大多数算法在头文件algorithm中.标准库还在头文件numer

  • 基于C++泛型编程职工管理系统

    目录 一.泛型编程思想 二.单链表是什么? 1.图示 2.链表的节点结构[节点类] 3.链表类 三.泛型编程核心 1.实现数据类 2.实现链表类 四.运行截图 1.主菜单 2.查看信息 3.更换数据类型 4.再次显示所有信息[抛转] 五.源码 前言: 前面介绍到了C++的泛型编程,并实现了万能容器,不过那使用的是数组,今天呢咱带大家实践一下使用泛型技术,结合单链表实现一个职工管理系统.保证大家看完之后有所感悟. 一.泛型编程思想 所谓泛型就是类型不固定,只需修改少量代码就可以扩展为其他类型的应用

  • C++使用泛型导致的膨胀问题

    目录 临峰不畏博主从事C++软件开发多年,由于之前的开发环境都是资源充足的服务器,不用考虑磁盘空间的问题.最近打算在智能家居主机的嵌入式平台上使用C++进行开发.FLASH存储空间有限,这是必须要考虑的因素,一定要重视. 如下定义两个list,元素类型不同: list<int> l1; list<string> l2; 如果是用C语来做应该怎么办?它会对应list<int>写一套代码,再对list<string>写一套.每套都有相同的成员函数,只是变量类型各

  • C++超详细讲解泛型

    目录 1.了解泛型编程 2.函数模板 2.1简单示例 2.2多个模板参数 2.3模板实例化 2.4模板和普通函数同时存在 2.5函数模板不支持定义和声明分离 3.类模板 3.1简单示例 3.2成员函数声明和定义分离 1.了解泛型编程 就好比活字印刷术,可以灵活调整印刷的板块和内容,比只能固定印刷某一个内容的雕版印刷术效率更高,也让印刷术由此得到了更广泛的应用. 在C++中,函数重载和模板的出现,让泛型编程得到了实际的应用.其中模板,就是类似活字印刷术一样的存在. 2.函数模板 八八了那么多没用的

  • C++ 泛型编程详解

    泛型编程与面向对象编程的目标相同,即使重用代码和抽象通用概念的技术更加简单.但是面向对象编程强调编程的数据方面,泛型编程强调的是独立于特定数据类型. 这一篇介绍一下 C++ 编程中与面向对象并列的另一大分支--泛型编程,这一篇主要介绍函数模板.类模板和成员模板三大部分 如有侵权,请联系删除,如有错误,欢迎大家指正,谢谢 泛型编程 模板是泛型编程的一种重要思想,STL(Standard Template Library,标准模板库)是采用模板实现的一个实例 函数模板 对比函数重载(同一作用域内函数

  • C++泛型编程Generic Programming的使用

    目录 一.容器 array vector deque list map 键值对key/value 二.迭代器iterator(泛型指针) 三.泛型算法Generic Programming insert()插入 erase()删除 find()用于无序搜索,搜素范围[first, last), 返回iterator, 找不到则返回last copy() 复制 泛型编程最初提出时的动机很简单直接:发明一种语言机制,能够帮助实现一个通用的标准容器库. 所谓通用的标准容器库,就是要能够做到,比如用一个

  • C++泛型编程函(数模板+类模板)

    目录 一.函数模板 1.函数模板介绍 2.函数模板与重载函数的关系 3.函数模板实现机制 二.类模板 1.类模板基本语法 2.类模板内函数的整体布局[分文件使用类模板] 3.类模板的static与模板类的static 4.数组实现万能容器 前言: 由于C++是静态语言,也就是说使用一个数据的时候必须先指定类型,这样的操作在编译后变量的类型是无法轻易改变的,就导致扩展性太差.或者一个函数需要很多次重载的时候,代码显得冗杂,由此产生了C++函数模板. 一.函数模板 1.函数模板介绍 ① 函数模板的产

  • C++ primer超详细讲解泛型算法

    目录 初识泛型算法 只读算法 写容器算法 定制操作 lambda表达式 lambda捕获和返回 再探迭代器 插入迭代器 iostream迭代器 反向迭代器 初识泛型算法 只读算法 只读取输入范围内的函数,不改变元素,find,accumula也是如此 (1)accumulate算法为求和算法,前两个参数指出求和元素范围,第三个是和的初值,例: int sum=accumulate(v.begin(),v.end(),0) (2)操作两个序列的算法 equal算法,确定两个序列是否保存相同的值,将

  • C语言超详细讲解排序算法上篇

    目录 1.直接插入排序 2.希尔排序(缩小增量排序) 3.直接选择排序 4.堆排序 进入正式内容之前,我们先了解下初阶常见的排序分类 :我们今天讲前四个! 1.直接插入排序 基本思想:当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排 序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移! 直接插入排序的特性总结: 1. 元素集

  • C++ primer超详细讲解顺序容器

    目录 顺序容器概述 容器库概览 迭代器 容器定义和初始化 赋值和swap 顺序容器操作 向顺序容器添加元素 访问元素 删除元素 特殊的forwa_list单向链表操作 改变容器大小 vector对象是如何增长的 定义:一个容器就是一个特定类型对象的集合. 顺序容器概述 (1)顺序容器类型 vector:可变数组大小,支持快速访问 deque:双端队列,支持快速随机访问,在头尾位置插入/删除速度很快 forward_list:单向链表,只支持单向顺序访问. array:固定大小数组,不能添加或删除

  • C++ primer超详细讲解关联容器

    目录 使用关联容器 关联容器概述 定义关联容器 pair类型 关联容器操作 关联容器迭代器 添加元素 删除元素 map的下标操作 访问元素 允许重复关键字的容器的名字开中都有包含单词multi,不保持关键字按顺序存储的容器的名字都以unordered开头. 使用关联容器 (1)经典的map程序 单词计数程序 map<string, size_t>word_count; string word; while (cin >> word) ++word_count[word]; for

  • C语言超详细讲解排序算法下篇

    上期学习完了前四个排序,这期我们来学习剩下的三个排序:

  • C语言 超详细讲解算法的时间复杂度和空间复杂度

    目录 1.前言 1.1 什么是数据结构? 1.2 什么是算法? 2.算法效率 2.1 如何衡量一个算法的好坏 2.2 算法的复杂度 2.3 复杂度在校招中的考察 3.时间复杂度 3.1 时间复杂度的概念 3.2 大O的渐进表示法 3.3 常见时间复杂度计算举例 4.空间复杂度 5. 常见复杂度对比 1.前言 1.1 什么是数据结构? 数据结构(Data Structure)是计算机存储.组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合. 1.2 什么是算法? 算法(Algorit

  • Java 超详细讲解十大排序算法面试无忧

    目录 排序算法的稳定性: 一.选择排序 二.冒泡排序 三.插入排序 四.希尔排序 五.堆排序 六.归并排序 七.快速排序 八.鸽巢排序 九.计数排序 十.基数排序 排序算法的稳定性: 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,如果排序以后,保证这些记录的相对次序保持不变,即在原序列中,a[i]=a[j],且 a[i] 在 a[j] 之前,排序后保证 a[i] 仍在 a[j] 之前,则称这种排序算法是稳定的:否则称为不稳定的. 一.选择排序 每次从待排序的元素中选择最小的元素,依次

  • C++ 数据结构超详细讲解顺序表

    目录 前言 一.顺序表是什么 概念及结构 二.顺序表的实现 顺序表的缺点 几道练手题 总结 (●’◡’●) 前言 线性表是n个具有相同特性的数据元素的有限序列.线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表.链表.栈.队列.字符串. 线性表在逻辑上是线性结构,也就是说连续的一条直线,但是在物理结构并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储. 本章我们来深度初体验顺序表 一.顺序表是什么 概念及结构 顺序表是一段物理地址连续的存储单元依次存储数据元素的线性

  • C语言超详细讲解字符串相乘

    目录 前言 一. 分析思路 二.使用步骤 1.代码如下 2.memset函数 三.总结 前言 我们已经知道,正常的两位整形数据通过*相乘,C语言中int为4字节,32bit(字节),其机器码第一位为符号位,余下31位表示数字,表示范围:-2^31(-2147483648)~2^31-1(2147483647),但超过了这个范围我们该如何做呢? 提示:将数字以字符串的形式进行操作 一. 分析思路 示例: 我们把每一个数都看成是一个字符串,每一个元素为十进制数字所对应的字 符,由于是后面的元素先进行

随机推荐