C++轮子 · STL算法和迭代器

STL的六大组件中最主要的是容器和算法这两个,一个泛化数据的存储,一个泛化数据的操作。前面两篇文章我们简单的介绍了STL中的容器,这篇文章将会介绍STL算法以及粘合容器和算法的迭代器。STL是基于模板实现,容器基于模板类,而算法基于模板函数。在具体介绍算法和迭代器之前,我们先简单的回顾一下模板函数的语法。

模板函数

模板函数的语法其实很简单,只要把正常的函数的参数类型或者返回值类型都参数化就可以了。比如选择两个数中的最大值,我们可以使用std::max

template< class T >
const T& max( const T& a, const T& b );

这个模板函数中两个参数的类型和返回值的类型都参数化了。

自动类型推导

模板函数有一个非常重要的特性是——它支持类型的自动推导。我们实例化一个模板类的时候需要手动指定模板参数类型:

std::vector<int> iv;

但是我们实例化一个模板函数却通常不需要,因为参数可以自动推导【1】:

std::max(1, 999);

当然有些情况下自动推导会失败(下面这个例子中,两个实参的类型不一样,自动推导有歧义),这个时候我们可以显式指定模板函数的参数类型:

std::max<double>(1, 2.0);

简化模板类的创建

模板函数参数的自动推导在使用上非常方便,这一点被广泛的用于模板类的工厂方法的实现(广义上任何用于创建类的实例的方法都可以称为工厂方法)。比如我们要构造一个std::pair,有两种方式。

第一,显式指定模板类参数:

std::pair<int, int> point(1, 2);

第二,使用模板函数自动推导:

std::make_pair(1, 2);

很显然后面这种方式用起来会比前面的方式舒服一些。标准库中存在大量的这一类型的工厂方法,比如:std::make_tuplestd::make_excpetion_ptrstd::make_shared<T>等等。

数据和操作

容器和算法的关系,实际上对应着数据和操作的关系。 计算机领域有一个非常著名的公式:

程序 = 数据结构 + 算法

换句话说,我们可以认为【2】:

程序 = 数据 + 操作

在STL中,容器抽象了数据的存储,算法抽象了数据的操作。当然这个说法其实并不准确,因为数据的操作还有一部分(比如插入删除等)直接放到了容器的内部作为成员函数而存在。操作应该实现为成员函数还是算法,主要取决于这个操作是否和数据存储相关(还有可能和效率相关)。理论上说和存储方式无关的操作都可以实现为算法【3】。

算法和数据类型、数据存储方式无关

下面这段算法的定义来自《算法导论》

An algorithm is a sequence of computational steps thats transform the input into the output.

翻译成中文是说

算法是把输入变成输出的一系列计算步骤

这段定义有几个隐含的点值得讨论:

1. 算法和输入数据的类型无关

当我们描述快速排序算法的时候,我们其实在描述排序的步骤,至于数据类型是int还是double和算法本身其实没有关系。这个概念在C语言中比较不好表达,通常需要通过void*加上函数指针来实现。比如C语言中的排序算法定义如下:

void qsort( void *ptr, size_t count, size_t size,int (*comp)(const void *, const void *) );

C++中因为模板函数的存在,参数类型可以被参数化,所以这个问题解决起来就方便很多,C++中的std::sort定义如下:

template< class RandomIt >
void sort( RandomIt first, RandomIt last );

现在,std::sort只需要实现算法逻辑,不需要考虑数据类型。 这个定义显然比C的定义要简单很多也清晰很多

2. 算法和输入的数据是如何存储无关【4】

我们描述一个算法,说的是数据的操作步骤,至于如何完成这些操作,实际上并没有规定。在C语言中,操作如何完成和数据如何存储有很大的关系,比如我们要实现指针的递增操作,我们的数据必须是连续存储的。但是在C++中,通过操作符的重载可以让数据的操作和数据如何存储的解耦开来。比如在C语言中线性查找算法的一个典型例子:查找一个字符串中的指定字符的函数strchr定义如下:

char *strchr( const char *str, int ch );

而在C++中,查找线性查找算法std::find定义如下:

template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );

strchar要求数据必须是连续存储,而且必须以\0结尾,而std::find却没有这个要求,你可以用它来查找链表中的数据。解耦数据的存储和算法实现的关键在于输入数据的泛化,而这个泛化的关键在于迭代器组成的区间。

区间

前面提到算法是把输入变成输出的计算步骤,所以要写一个范型算法,首先要解决的问题是如何表达输入。一个范型算法的输入通常是由两个迭代器组成的左闭右开的区间表示的,C语言中则通常是一个首地址+长度这种方式。使用半开闭区间的方式有下面这些好处:

  • 空集的概念很好表示,首尾相同即可 [beg, beg)
  • 比较容易返回错误值,数据查找,如果没有找到,我们不需要返回一个特殊值,直接返回end,就可以了,因为end不在区间内部,返回它很好的表达没有找到这个概念。
  • 比较容易表达迭代终止条件这个概念,beg == end即可表示迭代终止,这对于迭代器来说是很重要的,因为它不需要支持算数操作符,只需要支持判等符即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/634241.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统:一些趣味小命令

目录 1、小火车 2、小牛 3、随机小趣物 4、开机自启出现 5、其他趣味图可复制 5.1 文字版 5.2 宗教信仰 5.3 手势 5.4 宇宙星际 5.5 其他 前提&#xff1a;先安装epel额外源 [rootlocalhost ~]#yum install epel-release.noarch -y 1、小火车 [rootlocalhost ~]#y…

JS封装本地缓存的设置,读取,移除,清空方法及使用示例

我封装了一个JS通用的缓存管理对象&#xff0c;可以提供缓存的设置&#xff0c;读取&#xff0c;移除&#xff0c;清空操作&#xff0c;使用也很方便&#xff0c;封装方法的代码在最下方。 Q: 为什么不直接用原生的缓存方法&#xff0c;要封装&#xff1f; A1:原生的缓存管理…

mac安装miniconda

1 intel芯片 mkdir -p ~/miniconda3 curl https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -o ~/miniconda3/miniconda.sh bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3 rm -rf ~/miniconda3/miniconda.sh初始化shell环境 ~/miniconda3/b…

Qt网络通信

1. UDP通信 1.1 udp通信的基本流程 创建套接字 绑定套接字 进行通信 关闭套接字 涉及到的类和信号 QUdpSocket&#xff1a;Udp套接字类&#xff0c;类对象就是一个udp套接字对象 QHostAddress&#xff1a;ip地址类 void readyRead()&#xff1a;信号&#xff0c;当有数据到达可…

[足式机器人]Part2 Dr. CAN学习笔记- Kalman Filter卡尔曼滤波器Ch05-3+4

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Kalman Filter卡尔曼滤波器 Ch05-34 3. Step by step : Deriation of Kalmen Gain 卡尔曼增益/因数 详细推导4. Priori/Posterrori error Covariance Martix 误差协方差矩阵 3. Step by step :…

gh0st远程控制——客户端界面编写(一)

1、新建一个基于对话框的MFC程序 ghost内核对unicode支持不好&#xff0c;所以不要勾选 在静态库使用MFC有助于我们的代码供别人使用 2、设置窗口可最大最小化 对话框 》右键属性 3、 为对话框添加列表 一个代表列表框架&#xff0c;一个代表日志框架 分别为2个控件添加唯…

[c++笔记]class,构造函数

1. class&#xff1a;即c的类&#xff0c;与C语言的结构体相近&#xff0c;与结构体相比较&#xff0c;其成员不只有变量&#xff0c;还可以为函数&#xff0c;即类的成员函数&#xff0c;成员函数与普通函数区别在于其作用范围取决于类[1]&#xff1b;通过类定义的内容叫做对象…

Umi3 创建,配置环境,路由传参(代码示例)

目录 创建项目 配置环境 创建脚手架 项目结构及其目录、 路由 配置路由 嵌套路由 编程式导航和声明式导航 声明式导航 编程式导航 约定式路由 路由传参 query传参&#xff08;问号&#xff09; 接收参数 params传参&#xff08;动态传参&#xff09; 接收参数 创…

精品量化公式——“筹码动态”,筹码动态改进版,增加了三个买点信号标识

不多说&#xff0c;直接上效果如图&#xff1a; ► 日线表现 代码评估 技术指标代码评估&#xff1a; 散筹估算: 使用EMA&#xff08;指数移动平均&#xff09;方法计算(WINNER(C*1.1)-WINNER(C*0.9))*70的3日均线&#xff0c;用黄色粗线表示。这是用于估算市场中散户投资者的…

【我与Java的成长记】之多态,重载与重写详解

系列文章目录 能看懂文字就能明白系列 C语言笔记传送门 Java笔记传送门 &#x1f31f; 个人主页&#xff1a;古德猫宁- &#x1f308; 信念如阳光&#xff0c;照亮前行的每一步 文章目录 系列文章目录&#x1f308; *信念如阳光&#xff0c;照亮前行的每一步* 前言一、多态的概…

gitlab 部署项目新分支

公司代码管理平台新切换到gitlab下&#xff0c;上线发版流程随之变更 1新建分支&#xff0c;开发完成&#xff0c;提交新分支 2.去gitlab平台上找到Merge requests 3 点击右上角的New merge request select source branch 选择新建的分支 点击 compare branches and contin…

navigateTo失效-跳转不了页面解决办法!uniapp\vue

改了一个小时多的错误&#xff0c;跳转页面无论怎么样都跳转不了&#xff0c;有2个问题&#xff1a; 注意&#xff1a;uniapp的报错可以在console里检查&#xff01; 1.pages.json文件没有配置路径&#xff0c; 在pages:[ ]里面加 &#xff08;根据自己的路径进行修改 {&qu…

d2l包安装教程

目录 一、下载d2l包 1、错误的安装方法 2、正确的安装方法 二、可能会遇到的问题 1、网络超时导致下载中断 2、windows powershell激活虚拟环境时报错 一、下载d2l包 直接按照教程安装 — 动手学深度学习 2.0.0 documentation运行命令pip install d2l0.17.6安装会比较慢&…

知识笔记(八十四)———链式语句中fetchSql和force和bind用法

fetchSql&#xff1a; fetchSql用于直接返回SQL而不是执行查询&#xff0c;适用于任何的CURD操作方法。 例如&#xff1a; $result Db::table(think_user)->fetchSql(true)->find(1);输出result结果为&#xff1a; SELECT * FROM think_user where id 1 force&#…

如何实现 H5 秒开?

我在简历上写了精通 H5&#xff0c;结果面试官上来就问&#xff1a; 同学&#xff0c;你说你精通 H5 &#xff0c;那你能不能说一下怎么实现 H5 秒 由于没怎么做过性能优化&#xff0c;我只能凭着印象&#xff0c;断断续续地罗列了几点&#xff1a; 网络优化&#xff1a;http2、…

CyberRT 一个多线程并发的bug

CyberRT 一个多线程并发的bug 场景 多线程去调用cyber GlobalData中Register接口&#xff0c;比如RegisterNode、RegisterChannel、RegisterService、RegisterTaskName&#xff0c;入参必须填相同的。 都会出现coredump。 分析 比如如下test代码&#xff1a; std::vector&l…

为什么需要放行回源IP

为什么需要放行回源IP 网站以“独享模式”成功接入WAF后&#xff0c;所有网站访问请求将先经过独享引擎配置的ELB然后流转到独享引擎实例进行监控&#xff0c;经独享引擎实例过滤后再返回到源站服务器&#xff0c;流量经独享引擎实例返回源站的过程称为回源。在服务器看来&…

C++初阶类与对象(三):详解复制构造函数和运算符重载

上次介绍了构造函数和析构函数&#xff1a;C初阶类与对象&#xff08;二&#xff09;&#xff1a;详解构造函数和析构函数 今天就来接着介绍新的内容&#xff1a; 文章目录 1.拷贝构造函数1.1引入和概念1.2特性 2.赋值运算符重载2.1运算符重载2.2放在哪里2.3运算符重载示例2.3.…

C++后端笔记

C后端笔记 资源整理一、高级语言程序设计1.1 进制1.2 程序结构基本知识1.3 数据类型ASCII码命名规则变量间的赋值浮点型变量的作用字符变量常变量 const运算符 二、高级语言程序设计&#xff08;荣&#xff09; 资源整理 C后端开发学习路线及推荐学习时间 C基础知识大全 C那…

【INTEL(ALTERA)】使用Quartus出现系统错误:找不到 MSVCR120.dll

说明 由于英特尔 Quartus Prime Pro Edition 软件 23.2 及更早版本存在一个问题&#xff0c;您在启动 Questa*-英特尔 FPGA Edition 时可能会看到此系统错误。此错误是由于安装 Questa*-英特尔 FPGA 版软件时未安装 Visual Studio 2013 的 Visual C 可再发行组件包。 此问题仅…