STL基本算法之copy与copy_backward

copy

        不论是对客端程序或对STL内部而言,copy()都是一个常常被调用的函数。由于copy进行的是复制操作,而复制操作不外乎应用assignment operator或者copy construct(copy 算法用的是前者),但是某些元素型别拥有的是trivial assignment operator,因此,如果能够使用内存直接复制行为(例如C标准函数memmove或者memcopy), 便能节省大量时间。为此,SGI STL的copy算法用尽各种办法,包括函数重载(function overloading)、型别特性(type traits)、偏特化(partial specialization)等编程技巧,无所不用其极地加强效率。下图表示了copy()操作的脉络。稍后配合源码进行说明;

        copy算法可将输入区间[first,last)内的元素复制到输出区间[result, result+(last-first))内,也就是说,它会执行赋值操作*result = *first,*(result+i)=*(first+i)直到first+i达到last。最终返回一个迭代器result + (last-first).copy对其template参数所要求的条件非常宽松。其输入区间只需要是InputIterator构成即可,输出区间只需要是OutputIterator构成即可。这意味着你可以使用copy算法,将任何容器的任何一段区间的内容,复制到任何容器的任何一段区间上。

        对于每个从0到last-first的整数n,copy执行赋值操作*(result+n) = *(first+n).赋值操作是向前操作的。

        如果输入区间和输出区间完全没有重叠,当然毫无问题,否则便需特别注意。如下图所示:

第二种情况当result位于first与last当中时,可能会出现错误。

从稍后即将显示的源码可知,copy元素时一一进行元素的赋值操作,如果输出区间的起点位于输入区间内,copy算法便(可能)会在输入区间的(某些)元素尚未被复制之前,就覆盖其值,导致错误的结果。在这里我们一再使用可能这个字眼,是因为,如故宫copy算法根据其所接收迭代器的特性决定调用memmove()来执行任务,就不会造成上述错误,因为memmove(),会考虑重叠的情况再进行处理,保证不会出现值还没被拷贝就被覆盖的情况。

下面的测试代码,是对上图copy的测试

#include <iostream>
#include <algorithm>
#include <deque>
#include <list>
using namespace std;template <class T>
struct display {void operator() (const T& x) { cout << x << ' '; }
};int main() {{int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};copy(ia+2, ia+7, ia);for_each(ia, ia+9, display<int>()); // 2 3 4 5 6 5 6 7 8 ;与预期正确结果相符cout << endl;}{int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};copy(ia+2, ia+7, ia+4);for_each(ia, ia+9, display<int>()); // 0 1 2 3 2 3 4 5 6 cout << endl;// 本例结果正确,因为调用copy算法使用memmove()执行了实际复制操作}{int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};deque<int> id(ia, ia+9);auto first = id.begin(); // 此处用了auto关键字,需要c++11才能编译通过auto last = id.end();++first;++first;cout << *first << endl;  // 2--last;--last;cout << *last << endl;  // 7auto result = id.begin();cout << *result << endl ; // 0copy(first, last, result);for_each(id.begin(), id.end(), display<int>());  // 2 3 4 5 5 6 7 8cout << endl;}{int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};list<int> id(ia, ia+9);auto first = id.begin(); // 此处用了auto关键字,需要c++11才能编译通过auto last = id.end();++first;++first;cout << *first << endl;  // 2--last;--last;cout << *last << endl;  // 7auto result = id.begin();advance(result, 4);  // result + 4cout << *result << endl ; // 4copy(first, last, result);for_each(id.begin(), id.end(), display<int>());  // 0 1 2 3 2 3 2 3 2cout << endl;// 本例结果错误,因为调用copy算法不是使用memmove执行实际复制操作}return 0;
}

        请注意,如果以vector取代上述的list进行测试,复制结果将是正确的,因为vector迭代器其实是个原生指针(native pointer),最终copy算法会以memmove()执行实际的复制操作。

        copy更改的是[result, result+(last-first))中迭代器所指对象,而非更改迭代器本身。它会为输出区间的元素赋予新值,而不是产生新的元素。它不能改变输出区间的迭代器个数。换句话说,copy不能直接用来将元素插入空容器中。

        如果我们想将元素插入(而非赋值)序列之内,那么明白使用序列容器的insert成员函数,要么使用copy算法搭配insert_iterator.

        现在我们来看看copy算法庞大的实现细节。下面是对外接口

template <class InputIterator, class OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) {return __copy_dispatch<InputIterator, OutputIterator>()(first, last, result);// 注意此处是先构造了对象,而后将参数传递给了给对象
}

        下面两个是多载函数,针对原始指针(可视为一种特殊的迭代器)const char*和const wchar_t*,进行内存直接拷贝操作:

inline char* copy(const char* first, const char* last, const char* result) {memmove(reset, first, last-first);return result + (last - first);
}inline wchar_t* copy(const wchar_t* first, const wchar_t*last, wchar_t* result) {memmove(reset, first, sizeof(wchar_t)*(last-first));return result + (last - first);
}

        copy函数的泛化版本中调用了一个__copy_dispatch()函数,此函数有一个完全泛化版本和两个偏特化版本:

//完全泛化版本
template <class InputIterator, class OutputIterator>
struct __copy_dispatch {OutputIterator operator()(InputIterator first, InputIterator last, OutputIterator result) {return __copy(first, last, result, iterator_category(first));}
};// 偏特化版本(1)
template <class T>
struct __copy_dispatch<T*, T*> {OutputIterator operator()(T* first, T* last, T* result) {typedef typename __type_traits<T>::has_trivial_assignment_operator t;return __copy_t(first, last, result, t());}
};// 偏特化版本(2)
template <class T>
struct __copy_dispatch<const T*, T*> {OutputIterator operator()(const T* first, const T* last, T* result) {typedef typename __type_traits<T>::has_trivial_assignment_operator t;return __copy_t(first, last, result, t());}
};

        这里必须从完全泛化版和偏特化版本的区别进行区分。首先__copy_dispatch()的完全泛化版根据迭代器种类的不同,调用了不同的copy(),为的是不同种类的迭代器所使用的循环条件不同,有快慢之别。

// InputIterator 版本
template <class InputIterator, class OutputIterator>
inline OutputIterator __copy(InputIterator first, InputIterator last, OutputIterator result, input_iterator_tag) {for (; first != last; ++result, ++first) *result = *first;return result;
}// RandomAccessIterator 版本
template <class InputIterator, class OutputIterator>
inline OutputIterator __copy(InputIterator first, InputIterator last, OutputIterator result, random_access_iterator_tag) {return copy_d(first, last, result, distance_type(first));
}template <class InputIterator, class OutputIterator, class Distance>
inline OutputIterator __copy_d(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, Distance*) {for (Distance n = last - first; n > 0; --n, ++result, ++first) *result = *first;return result;
}

        这是__copy_dispatch()完全泛化的故事。现在看看连个偏特化版本。这连个偏特化版本再"参数为原生指针形式"的前提下,希望进一步探测"指针所指之物是否具有trivial assignment operator(平凡赋值操作符)".这一点对效率影响不小,因为这里的复制操作是有assignment操作符负责,如果指针所指对象拥有non-trivial assignment operator,复制操作就一定得通过它来进行。但如果指针所指对象拥有的是trivial assignment operator,复制操作可以不通过它,直接以最快速的内存拷贝方式(memmove())完成。C++语言本身无法让我们侦测某个对象的型别是否具有trivial assignment operator,但是SGI STL采用__type_traits<>编程技巧来弥补,参见迭代器与traits编程技法-CSDN博客 。注意,通过增加一层间接性的手法。我们得以区分两个不同的copy_t():

//以下版本适用于所指对象具备trivial assignment operator
template <class T>
inline T* __copy_t(const T* first, const T* last, T* result, __true_type) {memmove(result, first, sizeof(T)* (last - first));return result + (last - first);
}//以下版本适用于所指对象具备non-trivial assignment operator
template <class T>
inline T* __copy_t(const T* first, const T* last, T* result, __false_type) {return __copy_d(first, last, result, (ptrdiff_t*)0);
}

        以上就死copy()的故事,一个无所不用其极地强化执行效率的故事。具体的测试用例,留给读者自行测试。

copy_backward

template<class BidrectionalIterator1, class BidrectionalIterator2>
inline BidrectionalIterator2 copy_backward(BidrectionalIterator1 first, BidrectionalIterator1 last, BidrectionalIterator2 result) ;

        这个算法的考虑以及实现的技巧与copy()十分类似,其操作示意图如下所示

        将[first,last)区间的每一个元素,以逆行的方式复制到result-1为起点,方向亦为逆行的区间上,换句话说,copy_backward算法会执行操作*(result-1)=*(last-1),*(result-2)=*(last-2),...依此类推。返回一个迭代器result - (last - first).copy_backward所接受的迭代器必须是BidrectionalIterator,才能够指针--。我们可以使用copy_backward算法,将任何一段区间的内容,复制到任何容器的任何一段区间上。关于区间重叠的问题,和copy函数处理是大体相当的逻辑,此处不在赘述,有兴趣的读者,可以再参考STL源码进行学习。

参考文档《STL源码剖析》--侯捷

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

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

相关文章

不可分割的整体—系统思考的微妙法则

不可分割的整体——系统思考的微妙法则 作为企业领导者&#xff0c;我们经常需要做出决策&#xff0c;但有时候&#xff0c;我们会忽略一个事实&#xff1a;每个决策都不是孤立的&#xff0c;它背后都是一个复杂系统的一部分。 无论是市场动态、团队协作&#xff0c;还是产品…

云计算基础-期末复习

第一章&#xff1a;云计算概论 一、云计算的定义与特征 1. 定义&#xff1a; 云计算是一种通过网络以按需、可扩展的方式获取计算资源和服务的模式。它将计算资源视为一种公用事业&#xff0c;用户可以根据需求动态获取和释放资源&#xff0c;而无需了解底层基础设施的细节。…

基于Java的小程序电商商城开源设计源码

近年来电商模式的发展越来越成熟&#xff0c;基于 Java 开发的小程序电商商城开源源码&#xff0c;为众多开发者和企业提供了构建个性化电商平台的有力工具。 基于Java的电子商城购物平台小程序的设计在手机上运行&#xff0c;可以实现管理员&#xff1b;首页、个人中心、用户…

【机器学习】机器学习的基本分类-监督学习-逻辑回归-对数似然损失函数(Log-Likelihood Loss Function)

对数似然损失函数&#xff08;Log-Likelihood Loss Function&#xff09; 对数似然损失函数是机器学习和统计学中广泛使用的一种损失函数&#xff0c;特别是在分类问题&#xff08;例如逻辑回归、神经网络&#xff09;中应用最为广泛。它基于最大似然估计原理&#xff0c;通过…

Milvus 2.5:全文检索上线,标量过滤提速,易用性再突破!

01. 概览 我们很高兴为大家带来 Milvus 2.5 最新版本的介绍。 在 Milvus 2.5 里&#xff0c;最重要的一个更新是我们带来了“全新”的全文检索能力&#xff0c;之所以说“全新”主要是基于以下两点&#xff1a; 第一&#xff0c;对于全文检索基于的 BM25 算法&#xff0c;我们采…

RHCE作业五-shell脚本

一要求&#xff1a; 通过shell脚本分析部署nginx网络服务 1.接收用户部署的服务名称 2.判断服务是否安装 ​ 已安装&#xff1b;自定义网站配置路径为/www&#xff1b;并创建共享目录和网页文件&#xff1b;重启服务 ​ 没有安装&#xff1b;安装对应的软件包 3.测试 判断服务…

分页查询日期格式不对

方式一:在属性上加入注解&#xff0c;对日期进行格式化 方式二:在 WebMvcConfiguration 中扩展Spring MVC的消息转换器&#xff0c;统一对日期类型进行格式化处理 /*** 统一转换处理扩展spring mvc* 后端返回前端的进行统一转化处理* param converters*/Overrideprotected voi…

深度学习3:数据预处理使用Pandas与PyTorch的实践

文章目录 导读一、主题与提纲1.1. 读取数据集1.2. 处理缺失值1.3. 转换为张量格式 二、结论 本文是经过严格查阅相关权威文献和资料&#xff0c;形成的专业的可靠的内容。全文数据都有据可依&#xff0c;可回溯。特别申明&#xff1a;数据和资料已获得授权。本文内容&#xff0…

Tülu 3:重新定义开源大模型的后训练范式

一、引言 在大型语言模型&#xff08;LLM&#xff09;的发展历程中&#xff0c;预训练阶段往往受到最多关注&#xff0c;动辄需要数百万美元算力投入和数万亿token的训练数据。然而&#xff0c;一个鲜为人知但同样关键的事实是&#xff1a;预训练完成的模型实际上并不能直接投…

【机器学习】机器学习的基本分类-监督学习-逻辑回归(Logistic Regression)

逻辑回归是一种分类算法&#xff0c;尽管名字中包含“回归”&#xff0c;但其主要用于解决二分类和多分类问题。它通过学习一个逻辑函数&#xff0c;预测输入属于某个类别的概率。 1. 逻辑回归的基本概念 目标 逻辑回归的目标是找到一个函数 h(x)&#xff0c;输出一个概率值 …

PyMOL操作手册

PyMOL 操作手册 The man will be silent, the woman will be tears. – itwangyang ​ 翻译整理&#xff1a;itwangyanng 2024 年 11月 29 日 目录 初识 PyMOL… 5 0.1 安装 PyMOL… 5 0.1.1 Windows 系统开源版 PyMOL 的安装… 5 0.1.2 教育版 PyMOL 的下载安装……

麒麟系统x86安装达梦数据库

一、安装准备前工作 操作系统&#xff1a;银河麒麟V10&#xff0c;CPU&#xff1a; x86_64 架构 下载地址&#xff0c;麒麟官网&#xff1a;https://www.kylinos.cn/ 数据库&#xff1a;dm8_20220915_x86_kylin10_64 下载地址&#xff0c;达梦数据库官网&#xff1a;https://…

Hot100 - 搜索二维矩阵II

Hot100 - 搜索二维矩阵II 最佳思路&#xff1a; 利用矩阵的特性&#xff0c;针对搜索操作可以从右上角或者左下角开始。通过判断当前位置的元素与目标值的关系&#xff0c;逐步缩小搜索范围&#xff0c;从而达到较高的效率。 从右上角开始&#xff1a;假设矩阵是升序排列的&a…

docker服务容器化

docker服务容器化 1 引言2 多个容器间网络联通2.1 单独创建关联2.2 创建时关联 3 服务搭建3.1 镜像清单3.2 容器创建 4 联合实战4.2 flink_sql之kafka到starrocks4.2 flink_sql之mysql到starrocks 5 文献借鉴 1 引言 ​ 利用docker可以很效率地搭建服务&#xff0c;本文在win1…

011变长子网掩码

变长子网掩码&#xff1a; 使用变长子网掩码&#xff08;VLSM&#xff09;优化地址分配 目标&#xff1a; 根据需求使用VLSM分配IP地址&#xff0c;减少浪费&#xff0c;并配置静态路由。 网络拓扑 创建一个包含三台路由器&#xff08;R1、R2、R3&#xff09;和五台PC&#x…

SpringBoot小知识(2):日志

日志是开发项目中非常重要的一个环节&#xff0c;它是程序员在检查程序运行的手段之一。 1.日志的基础操作 1.1 日志的作用 编程期调试代码运营期记录信息&#xff1a; * 记录日常运营重要信息(峰值流量、平均响应时长……) * 记录应用报错信息(错误堆栈) * 记录运维过程数据(…

大数据新视界 -- 大数据大厂之 Hive 数据安全:权限管理体系的深度解读(上)(15/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

智能探针技术:实现可视、可知、可诊的主动网络运维策略

网络维护的重要性 网络运维是确保网络系统稳定、高效、安全运行的关键活动。在当今这个高度依赖信息技术的时代&#xff0c;网络运维的重要性不仅体现在技术层面&#xff0c;更关乎到企业运营的方方面面。网络运维具有保障网络的稳定性、提升网络运维性能、降低企业运营成本等…

RT-DETR融合Inner-IoU及相关改进思路

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《Inner-IoU: More Effective Intersection over Union Loss with Auxiliary Bounding Box》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/abs/2311.02877 代码链接&a…

在Springboot项目中实现将文件上传至阿里云 OSS

oss介绍 阿里云对象存储服务&#xff08;OSS&#xff09;是一种高效、安全和成本低廉的数据存储服务&#xff0c;可以用来存储和管理海量的数据文件。本文将教你如何使用 Java 将文件上传到阿里云 OSS&#xff0c;并实现访问文件。 1. 准备工作 1.1 开通 OSS 服务 登录阿里云…