STL源码剖析 基本算法 < stl_algobase.h >

注意事项 :

  • 实际使用的时候,使用的是<algorithm>这个头文件,不是题目中的< stl_algobase.h > 

equal函数

  • 如果两个序列在[firsLlast) 区间内相等,equal() 返 回 true.如果第二序列的元素比较多,多出来的元素不予考虑。因此,如果我们希望保证两个序列完全相等,必须先判断其元素个数是否相同
    int ia[9] = {0,1,2,3,4,5,6,7,8};std::vector<int> iv1(ia,ia+5);std::vector<int> iv2(ia,ia+9);if(iv1.size() == iv2.size() &&std::equal(iv1.begin(),iv1.end(),iv2.begin())){}
  • 抑或使用容器所提供的equality操作符,例如 vecl = =vec2.如果第二序列的 元素比第一序列少,这个算法内部进行迭代行为时,会超越序列的尾端,造成不可预测的结果
  • 第一版本缺省采用元素型别所提供的equality操作符来进行大小比 较,第二版本允许我们指定仿函数pred作为比较依据

 fill

  • 将 [first/ last) 内的所有元素改填新值. 

    int ia[9] = {0,1,2,3,4,5,6,7,8};std::vector<int> iv1(ia,ia+5);std::vector<int> iv2(ia,ia+9);std::fill(iv2.begin(),iv2.end(),-1);for (auto elem:iv2) {std::cout<< elem << ' ';}std::cout<< "\n";

 fill_n

  • 将[firsllast)内的前n 个元素改填新值,返回的迭代器指向被填入的最后一个元素的下一位置。

  •  如果n 超越了容器的现有大小,会造成什么结果?例如

  •  我们很容易就可以从源代码知道,由于每次迭代进行的是assignment操作,是一种覆写(overwrite)操作,所以一旦操作区间超越了容器 大小,就会造成不可预期的结果。解决办法之一是,利用inserter ()产生一个具 有插入(insert)而非覆写(overwrite)能力的迭代器
  • inserter( ) 可产生一个用来修饰迭代器的配接器(iterator adapter)。用法如下
int main(int argc,char* argv[]) {int ia[9] = {0,1,2};std::vector<int> iv1(ia,ia+3);std::fill(iv1.begin(),iv1.end(),-1);std::fill_n(iv1.begin(),5,7);for (auto elem:iv1) {std::cout<< elem << ' ';}std::cout<< "\n";
}
  •  但是实际使用的时候 并没有出现问题

 iter_swap

  •  iter_swap( ) 是 “迭代器之valuetype派上用场的一个好例子。是的,该函数 必须知道迭代器的veluetype,才能够据此声明一个对象,用来暂时存放迭代器所 指对象。为此,上述源代码特别设计了一个双层构造,第一层调用第二层,并多出一个额外的参数value_type(a) 这么一来,第二层就有VClIue type可以用了。
  • 乍见之下你可能会对这个额外参数在调用端和接受端的型别感到讶异,调用端是value_type (a), 接受端却是T*。只要找出value_type ()的定义瞧瞧,就一点也不奇怪了:
#include <iostream>
#include <algorithm>
#include <vector>
#include <random>
#include <functional>template<class ForwardIt>
void selection_sort(ForwardIt begin,ForwardIt end){for (ForwardIt i = begin;i!=end;++i) {std::iter_swap(i,std::min_element(i,end));}
}
int main(int argc,char* argv[]) {std::random_device rd{};std::mt19937 gen(rd());std::uniform_int_distribution<>dist(-9,9);std::vector<int>v{};std::generate_n(std::back_inserter(v),20,std::bind(dist,gen));std::cout << "Before sort:" << std::showbase;for (auto e:v) {std::cout<<e<<' ';}selection_sort(v.begin(),v.end());std::cout<<"\nAfter sort:";for (auto e:v) {std::cout << e << ' ';}}

lexicographic al_.com pare

  • 以 “字典排列方式”对两个序列[firstlAastl)和 [first2,last2)itt行比较。比较操作针对两序列中的对应位置上的元素进行,并持续直到(1 )某一组对应元素彼此不相等;(2 )同时到达last1 和 last2 (当两序列的大小相同);⑶ 到 达 lastl或last2 (当 两 序 列 的 大 小 不 同 )。
  • 第一序列以字典排列方式(lexicographically)而言不小于第二序列

  •  第二版本允许你指定一个仿函数comp作为比较操作之用,取代元素型别所提 供 的 less-than (小 于 )操 作 符 

#include <iostream>
#include <algorithm>
#include <vector>
#include <random>int main(int argc,char* argv[]) {std::vector<char>v1{'a','b','c','d'};std::vector<char>v2{'a','b','c','d'};std::mt19937 g{std::random_device{}()};while (!std::lexicographical_compare(v1.begin(),v1.end(),v2.begin(),v2.end())){for (auto c:v1) std::cout << c << ' ';std::cout << ">= ";for (auto c:v2) std::cout << c << ' ';std::cout << "\n";std::shuffle(v1.begin(),v1.end(),g);std::shuffle(v2.begin(),v2.end(),g);}for (auto c:v1) std::cout << c << ' ';std::cout << "< ";for (auto c:v2) std::cout << c << ' ';std::cout << "\n";
}

mism atch

  • 用来平行比较两个序列,指出两者之间的第一个不匹配点。返回一对迭代器;分别指向两序列中的不匹配点,如下图。
  • 如果两序列的所有对应元素都匹配,返回的便是两序列各自的last迭代器。
  • 缺省情况下是以equality操作符来比较元素; 但第二版本允许用户指定比较操作。如果第二序列的元素个数比第一序列多,多出来的元素忽略不计。如果第二序列的元素个数比第一序列少,会发生未可预期的行为。

#include <iostream>
#include <algorithm>
#include <vector>
#include <random>bool mypredicate(int i,int j){return (i==j);
}int main(int argc,char* argv[]) {std::vector<int>my_vector{};for (int i = 1; i < 6; ++i) {my_vector.emplace_back(i*10);//10 20 30 40 50 }int myints[] = {10,20,80,320,1024};std::pair<std::vector<int>::iterator,int*>mypair;mypair = std::mismatch(my_vector.begin(),my_vector.end(),myints);std::cout << "First mismatching elements: " << *mypair.first;std::cout << " and " << *mypair.second << std::endl;++mypair.first;++mypair.second;mypair = std::mismatch(mypair.first,my_vector.end(),mypair.second, mypredicate);std::cout << "Second mismatching elements: " << *mypair.first;std::cout << " and " << *mypair.second << std::endl;return 0;
}

copy----- 强化效率无所不用其极

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

  •  copy算法可将输入区间[first, last)内的兀素复制到输出区间[result, result+(last-first)) 内• 也就是说,它会执行赋值操作 *result = *first, * (result + 1) = * ( f irst + 1) , … 依 此 类 推 。 返 回 一 个 迭 代 器 : result+(last-first)= copy 对其template参数所要求的条件非常宽松。其输入区间只需由Inputiterators构成即可,输出区间只需由Outputiterator构成即可。
  • 这意味着你可以使用copy算法,将任何容器的任何一段区间的内容,复制到任何 容器的任何一段区间上

  •  如果输入区间和输出区间完全没有重叠,当然毫无问题,否则便需特别注意•为什么图6-3第二种情况(可能)会产生错误?从稍后即将显示的源代码可知,copy 算法是一一进行元素的赋值操作,如果输出区间的起点位于输入区间内,copy算法便(可能)会在输入区间的(某些)元素尚未被复制之前,就覆盖其值,导致错误结果.在这里我一再使用“可能”这个字眼,是因为,如果copy算法根据其所 接收的迭代器的特性决定调用 memmove() 来执行任务,就不会造成上述错误,因 为 memmove() 会先将整个输人区间的内容复制下来,没有被覆盖的危险
    int ia[] = {0,1,2,3,4,5,6,7,8};//输出区间的终点和输入的区间重叠  没有问题std::copy(ia+2,ia+7,ia);std::for_each(ia,ia+9,display<int>());std::cout << "\n";
    // 以下,输出区间的起点与输入区间重叠,可能会有问题int ia[] = {0,1,2,3,4,5,6,7,8};std::copy(ia+2,ia+7,ia+4);std::for_each(ia,ia+9,display<int>());std::cout << "\n"; //0 1 2 3 2 3 4 5 6
    int ia[] = {0,1,2,3,4,5,6,7,8};//deque 拥有 RandomAcCGSSltGrCltorstd::deque<int>id(ia,ia+9);std::deque<int>::iterator first = id.begin();std::deque<int>::iterator last = id.end();++++first;  //advance(first,2);std::cout << *first << std::endl; //2----last;   //advance(last,-2);std::cout << *last << std::endl;  //7std::deque<int>::iterator result = std::begin(id);std::cout << * result << std::endl;// 以下,输出区间的终点与输入区间重叠,没问题std::copy(first,last,result);//(2 3 4 5 6) 5 6 7 8std::for_each(id.begin(),id.end(),display<int>{});std::cout << "\n";return 0;
    int ia[] = {0,1,2,3,4,5,6,7,8};//deque 拥有 RandomAcCGSSltGrCltorstd::deque<int>id(ia,ia+9);std::deque<int>::iterator first = id.begin();std::deque<int>::iterator last = id.end();++++first;  //advance(first,2);std::cout << *first << std::endl; //2----last;   //advance(last,-2);std::cout << *last << std::endl;  //7std::deque<int>::iterator result = std::begin(id);std::advance(result,4);std::cout << * result << std::endl;//以下,输出区间的起点与输入区间重叠,可能会有问题//本例结果错误,因为调用的 copy算法不再使用memmove ()执行实际复制操作std::copy(first,last,result);//0 1 2 3 (2 3 4 5 6std::for_each(id.begin(),id.end(),display<int>{});std::cout << "\n";return 0;
  • 请注意,如果你以vector取代上述的deque进行测试,复制结果将是正确的,因 为 vector迭代器其实是个原生指针(native pointer), 见4.2.3节,导致调用的copy算法以memmove ()执行实际复制操作
  • copy更改的是[result,result + (last-first)) 中的迭代器所指对象,而非更改迭代器本身。它会为输出区间内的元素赋予新值,而不是产生新的元素。它不能改变输出区间的迭代器个数。换句话说 , copy不能直接用来将元素插入空容器中。例子如下所示:如果不给myvector申请空间就会出错,只有申请空间之后初始化为0,然后执行覆盖原有数值的操作
  • 如果你想要将元素插入(而非赋值)序列之内,要么明白使用序列容器的insert 成员函数,要么使用 copy 算法并搭配 insert_iterator (8.3.1 节 )
  • 现在我们来看看 copy算法庞大的实现细节。下面是冰山一角,也是唯一的对外接口:
int main(int argc,char* argv[]) {int myints[]={10,20,30,40,50,60,70};std::vector<int>myvector(7);std::copy(myints,myints+7,myvector.begin());std::cout << "myvector contains:";for (std::vector<int>::iterator it = myvector.begin();it != myvector.end();++it) {std::cout << ' '<< *it;}std::cout << "\n";return 0;
}

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

  •  这里必须兵分两路来探讨。首先,_ copy_dispatch ()的完全泛化版根据迭代器种类的不同,调用了不同的 一copy()> 为的是不同种类的迭代器所使用的循 环条件不同,有快慢之别.

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

  • 第三个问题的解答是:我们以为vector的迭代器是random access iterator,没想到它事实上是个T* ;vector迭代器其实是原生指针。这就怪不得copy。 一旦面对vector 迭代器,就往T*的方向走去了;

 6.4.4 copy_backw ard

  • 将[firstaast)区间内的每一个元素,以逆行的方向复制到 以 result-1为起点,方向亦为逆行的区间上。换句话说,copy_backward算法会
    执行赋值操作 * (result-1) = * (last-1) , * (result-2 ) = * (last-2 ) , 依此类推。
  • 返回一个迭代器:result- (last-first) □ copy_backward所接受的迭代器必
    须 是 Bidirectionallterators, 才 能 够 “倒行逆施”。你可以使用copy_backward算
    法,将任何容器的任何一段区间的内容,复制到任何容器的任何一段区间上°如果输
    入区间和输出区间完全没有重叠,当然毫无问题,否则便需特别注意

 

    int ia[] = {0,1,2,3,4,5,6,7,8};//输出区间的终点和输入区间重叠 没问题std::copy_backward(ia+2,ia+7,ia+9);std::for_each(ia,ia+9,display<int>{});//0 1 2 3 2 3 4 5 6std::cout<<std::endl;return 0;
    int ia[] = {0,1,2,3,4,5,6,7,8};//输出区间的起点和输入区间重叠 可能会有问题std::copy_backward(ia+2,ia+7,ia+5);std::for_each(ia,ia+9,display<int>{});//2 3 4 5 6 5 6 7 8//本例结果正确,因为调用的copy算法使用memmove ()执行实际复制操作std::cout<<std::endl;return 0;

 

template<class T>
struct display{void operator()(const T&x){std::cout << x << ' ';}
};
int main(int argc,char* argv[]) {int ia[] = {0,1,2,3,4,5,6,7,8};std::deque<int>id(ia,ia+9);std::deque<int>::iterator first = id.begin();std::deque<int>::iterator last = id.end();++++first; //advance(first,2);std::cout << *first << std::endl;----last;  //advance(last,-2);std::cout << *last << std::endl;std::deque<int>::iterator result = id.end();//输出区间的终点和输入区间重叠 没有问题std::copy_backward(first,last,result);std::for_each(id.begin(),id.end(),display<int>{});//0 1 2 3 2 3 4 5 6std::cout<<std::endl;return 0;
}
int main(int argc,char* argv[]) {int ia[] = {0,1,2,3,4,5,6,7,8};std::deque<int>id(ia,ia+9);std::deque<int>::iterator first = id.begin();std::deque<int>::iterator last = id.end();++++first; //advance(first,2);std::cout << *first << std::endl;----last;  //advance(last,-2);std::cout << *last << std::endl;std::deque<int>::iterator result = id.begin();std::advance(result,5);std::cout << *result << std::endl;//输出区间的起点和输入区间重叠 可能会有问题std::copy_backward(first,last,result);// 本例结果错误,因为调用的copy算法不再使用memmove()执行实际复制操作  但是我实际编码结果是正确的std::for_each(id.begin(),id.end(),display<int>{});//2 3 4 5 6 5 6 7 8std::cout<<std::endl;return 0;
}

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

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

相关文章

华为弹性云服务器ECS使用学习0

学习大纲 ECS概述 组成&#xff1a;CPU,内存&#xff0c;镜像&#xff0c;操作系统&#xff0c;云硬盘 ECS产品优势 弹性伸缩AS&#xff08;弹性可扩展&#xff09; ECS产品架构 Region:地理位置和网络时延的划分&#xff0c;同一个Region中共享计算和存储资源&#xff…

STL源码剖析 set相关算法

STL 一共提供了四种与set (集合)相关的算法&#xff0c;分别是并集(union)、交集(intersection) > 差集 (difference)、对称差集 (symmetricdifference所谓set,可细分为数学上的定义和STL的定义两种&#xff0c;数学上的set允许元素重复而未经排序&#xff0c;例 如 &#x…

C++ 使用递增的方式初始化 一个 vector

int countOdds(int low, int high) {int count 0;std::vector<int>temp{high-low1,0};int n low;std::generate(temp.begin(),temp.end(),[&]{return n;});for (auto x:temp) {std::cout << x;}} 使用Itoa std::iota int countOdds(int low, int high) {in…

Python学习4 列表基础知识和常用函数

列表 1.格式 2.增删改查 列表下标&#xff1a; 0–n-1 -n-(-1) #对列表进行切片 #0-(n-1) #-n-(-1) list[dq,python,mm] print(list[0:2])#[0,2) print(list[-3:-2])#[-3,-2) #输出 #[dq, python] #[dq]题目&#xff1a; 【1&#xff1a;4&#xff1a;2】:[1,4),步长为2 下…

Python学习5 元组基础知识和常用函数

元组概念 元组&#xff1a;a&#xff08;1&#xff0c;23&#xff09; 列表&#xff1a;a [1,23] 创建和访问元组 Python 的元组与列表类似&#xff0c;不同之处在于tuple被创建后就不能对其进行修改&#xff0c;类似字符串。 元组与列表类似&#xff0c;也用整数来对它进行…

STL源码剖析 仿函数

仿函数 也叫函数对象1&#xff0c;具有函数性质的对象&#xff1b;2&#xff0c;这种东西在调用者可以像函数一样地被调用(调用)&#xff0c;在被调用者则以对象所定义的function call operator扮 演函数的实质角色。要将某种 “操作”当做算法的参数&#xff0c;唯一办法就是先…

Python学习6 字典基础知识和常用函数

字典概念 字典是 Python 提供的一种常用的数据结构&#xff0c;它用于存放具有映射关系的数据。为了保存具有映射关系的数据&#xff0c;Python 提供了字典&#xff0c;字典相当于保存了两组数据&#xff0c;其中一组数据是关键数据&#xff0c;被称为 key&#xff1b;另一组数…

Java web后端2 Servlet Maven HttpServlet ServletConfig ServletContext HTTP协议

创建项目 新建项目 Java Enterprise JDK1.8 Web Application Tomcat JAVA 默认 过程需要联网 Maven的配置 IDEA内置Maven 修改本地仓库位置&#xff0c;因为以后会越来越大 替换配置文件&#xff1a; 阿里云镜像下载 Servlet基础 1.动态Web资源开发 2.Servlet是使用J…

STL源码剖析 配接器

配接器(adapters)在 STL组件的灵活组合运用功能上&#xff0c;扮演着轴承、转换器的角色。Adapter这个概念&#xff0c;事实上是一种设计模式(design pattern)。 Design Patterns)) 一书提到23个最普及的设计模式&#xff0c;其中对odopter样式的定义如下&#xff1a;将 一个cl…

中科大 计算机网络3 网络边缘Edge

网络结构 边缘系统 网络核心 接入网 方块&#xff1a;边缘系统(主机) 圆的&#xff1a;网络核心&#xff0c;数据交换作用 连接边缘系统和网络核心的叫做接入网&#xff08;access&#xff09;&#xff0c;把边缘的主机接入到网络核心&#xff08;所以是分布式的&#xff09; …

STL源码剖析 入门开始 STL概论与版本简介

源代码之中时而会出现一些全局函数调用操作&#xff0c;尤其是定义于<stl_construct.h> 之中用于对象构造与析构的基本函数&#xff0c;以及定义于<stl_uninitialized.h>之 中 用 于 内 存 管 理 的 基 本 函 数 &#xff0c; 以及定义于<stl_algobase.h>之中…

中科大 计算机网络4 网络核心Core 分组交换 电路交换

网络核心 电路交换&#xff08;线路交换&#xff09;&#xff1a;打电话之前&#xff0c;先建立一条链路&#xff08;物理&#xff09; 分组交换&#xff1a;存储转发的方式 电路交换&#xff08;线路交换&#xff09; 通过信令&#xff08;控制信息&#xff0c;如&#xf…

STL 源码剖析 空间配置器

以STL的运用角度而言&#xff0c;空间配置器是最不需要介绍的东西&#xff0c;它总是隐藏在一切组件&#xff08;更具体地说是指容器&#xff0c;container&#xff09; 的背后但是STL的操作对象都存放在容器的内部&#xff0c;容器离不开内存空间的分配为什么不说allocator是内…

中科大 计算机网络7 分组延迟 分组丢失 吞吐量

分组丢失和延迟的原因 队列太长没有意义&#xff0c;用户需求 排队&#xff1a;输出能力<到来的分组&#xff0c;需要等待 四种分组延迟 节点处理延迟&#xff1a;确定的 排队延迟&#xff1a;随机&#xff0c;取决于网络情况 一个比特的传输时间&#xff1a; R1Mbps …

STL源码剖析 迭代器iterator的概念 和 traits编程技法

iterator模式定义如下&#xff1a;提供一种方法&#xff0c;使之能够依序巡访某个 聚合物(容器)所含的各个元素&#xff0c;而又无需暴露该聚合物的内部表述方式.STL的中心思想在于&#xff1a;将数据容器(containers)和算法(algorithms)分开&#xff0c;彼此独立设计&#xff…

中科大 计算机网络11 应用层原理

应用层大纲 传输层向应用层提供的服务&#xff0c;形式是Socket API&#xff08;原语&#xff09; 一些网络应用的例子 互联网层次中&#xff0c;应用层协议最多 流媒体应用&#xff1a;直播 网络核心最高的层次就是网络层 应用进程通信方式 C/S&#xff1a; 客户端&…

STL源码剖析 序列式容器 vector 和 ilist

Vector list 单向链表 ilistlist的删除操作&#xff0c;也只有指向被删除元素的迭代器会失效&#xff0c;其他迭代器不会受到影响

中科大 计算机网络5 接入网和物理媒体

接入网 接入网&#xff1a;把边缘&#xff08;主机&#xff09;接入核心&#xff08;路由器&#xff0c;交换机&#xff09; 骨干网【连接主机和主机】和接入网中都有物理媒体 接入方式&#xff1a;有线和无线 带宽共享/独享 接入网&#xff1a;住宅接入modem modem调制解调…

STL源码剖析 序列式容器 deque双端队列

相较于vector的内存拷贝&#xff0c;deque在内存不足时只需要进行内存的拼接操作即可&#xff0c;不需要重新配置、复制、释放等操作&#xff0c;代价就是迭代器的架构不是一个普通的指针&#xff0c;比较复杂d e q u e 的迭代器 deque是分段连续空间。维持其“整体连续”假象…

中科大 计算机网络6 Internet结构和ISP

互联网的结构 端系统通过接入ISPs接入互联网 n个ISP互相连接&#xff1a; IXP,Internet exchage point:互联网接入点&#xff0c;互联网交互点 ISP&#xff1a;互联网服务提供商&#xff0c;提供接入&#xff0c;提供网络【中国移动&#xff0c;中国电信】 ICP&#xff1a…