STL源码刨析:序列式容器之vector
目录
1.序列式容器和关联式容器
2.vector的定义和结构
3.vector的构造函数和析构函数的实现
4.vector的数据结构以及实现源码
5.vector的元素操作
前言
本系列将重点对STL中的容器进行讲解,而在容器的分类中,我们将容器分为序列式容器和关联式容器。本章作为容器的实现源码的讲解,将简单介绍这两种类型的容器的区别,再对每一个类型所含的容器的实现源码进行讲解。
序列式容器和关联式容器
序列式容器:按照元素的添加顺序来组织元素。提供了一种线性的数据结构,可以快速何位置的元素,插入和删除操作可能需要移动其他元素
关联式容器:通过键值对来存储元素,并且通常使用某种形式的平衡树或哈希表来组织元素。特别是对于大量数据,关联式容器在查找、插入和删除操作上通常具有较高的效率
序列式容器和关联式容器的区别:
| 序列式容器 | 关联式容器 | |
| 存储方式 | 元素添加顺序存储 | 按键的顺序或哈希值存储 |
| 访问速度 | 支持快速随机访问 | 不支持快速随机访问 |
| 插入和删除 | 在插入和删除时要移动元素,可能较慢 | 能提供快速的插入和删除操作 |
| 元素唯一性 | 不保证元素的唯一性 | 保证元素的唯一性 |
| 迭代器类型 | 随机访问迭代器 | 双向迭代器或正向迭代器 |
表1.序列式容器和关联式容器的区别
vector的定义和结构
vector属于序列式容器,定义在头文件<vector>中,但是SGI STL将其实现放在更底层的<stl_vector.h>头文件中。vector和数组很像,但是vector属于动态存储空间,能自动随着元素的插入而自行扩充空间以容纳新的元素,数组只能通过new或者malloc重新分配更大的空间,并将元素移动到新的空间。
在阅读过《STL源码刨析:迭代器概念与Traits编程方法》后,我们应该清楚以下几点:
1.每一个容器和具体的实现算法是分开定义的,而为了将容器和算法串联在一起,我们要为其定义迭代器(PS:vector的迭代器类型为随机迭代器,因为使用vector容器时支持下标操作)
2.针对迭代器的类型,我们还需要对其封装并判断传入模板的类型(Traits编程方法)
3.每一个容器都需要为其分配内存空间,所以我们还需要一个空间配置器
在以上三点的基础上,我们还需要对容器定义三个迭代器,分别指向使用空间的头,使用空间的尾和空闲空间的尾。所以我们的vector容器的结构应该大体如下:
//vector容器的大体结构
template<class T, class Alloc = alloc> //alloc是默认的配置器
class vector{
public:typedef T value_type; //传入的参数的类型 typedef value_type* pointer; //传入的参数的指针typedef value_type* iterator; //传入的参数的迭代器typedef value_type& reference; //传入的参数的引用typedef size_t size_type; //传入的大小typedef ptrdiff_t difference_type; //传入的元素之间的距离
protector:typedef simple_alloc<value_type, Alloc> data_allocator; //配置器的定义iterator start; //表示可用空间的头iterator finish; //表示可用空间的尾iterator end_od_storage;//表示空闲空间的尾
}//simple_alloc配置器的实现
template<class T, class Alloc = alloc>
class simple_alloc{
public://分配空间static T* allocate(size_t n){ return 0 == n ? (T*)Alloc:: allocate(n * sizeof(n)); }static T* allocate(void){ return (T*)Alloc :: allocate(sizeof(T)); }//释放空间statice T* deallocate(T* p, size_t n){if(0 != n){ Alloc::deallocate(p,n * sizeof(T));}}statice T* deallocate(T* p){ Alloc::deallocate(p,sizeof(n));}
}
vector的构造函数和析构函数的实现
vector作为常用的容器类型,无论是在项目中还是在算法题中常常出现,所以我们对其构造函数并不陌生,以下便是的构造函数和析构函数的实现(PS:千万不要在容器中存放指针,指针如果是使用new进行分配的,并不能直接通过调用vector的析构函数对其元素进行释放,必须得遍历整个容器依次释放,否则会存在内存泄漏):
/* 针对代码中的uninitalized_fill_n()和destroy函数请参见《STL源码刨析:空间配置器(allocator)》中内存基本处理函数 *///vector容器的构造函数
vecotr() : start(0), finish(0), end_of_storage(0){} //列表初始化
vector(int n, const T& value){ fill_initialize(n,value); }
vector(long n,const T& value){ fill_initialize(n,value); }
explicit vector(szie_type n){ fill_initialize(n,T()); } //explicit用于禁止隐式转换
vector(size_type n, const T& value){ fill_initialize(n,value); }void fill_initialize(size_type n,const T& value){start = allocate_and_fill(n,value); //使用配置器分配空间finish = start + n;end_of_storage = finish;
}iterator allocate_and_fill(size_type new_size, const T& x){iterator result = data_allocator::allocate(n); //分配空间uninitalized_fill_n(result,n,x); return result;
}//vector容器的析构函数
~vector(){destroy(start,finish);deallocate();
}void deallocate(){if(start){data_allocator::deallocate(start, end_of_storage - start); //释放空间}
}
vector的数据结构以及实现源码
vector容器的数据结构采用的是线性连续空间,以定义的迭代器start和finish分别指向以及使用的空间的头部和尾部(范围),并以迭代器end_of_storage指向分配的空间的尾部(PS:start和finish指向的是使用的空间,end_of_storage指向的分配的全部空间的尾部),使用这三个迭代器我们将可以使用首尾元素,容器大小,整体容量,空容器的判断,下标运算符[],头元素和尾元素的值的接口函数,整体实现如下:
//返回头元素指针
iterator begin(){ return start; }
//返回尾元素指针
iterator end() { return finish; }
//返回容器使用的空间大小
size_type size() const { return szie_type(end() - begin());}
//返回容器的全部空间大小
size_type capacity() const {return size_type(end_of_storage - begin());
}
//返回容器是否为空
bool empty() const { return begin() == end(); }
//返回指定下标的元素
reference operator[](size_type n){ return *(begin() + n); }
//返回容器的头元素的值
reference front(){ return *begin(); }
//返回容器的尾元素的值
reference back() {return *(end() - 1); }
阅读本段,可能会产生疑问。全部空间是什么?已经使用的空间是什么?为什么back返回的值是尾指针-1后的值?针对这些疑问,获取阅读下图便会清晰:

图1.vector的数据结构
参考上图可知,迭代器finish指向的并不是最后一个元素的地址,而是指向最后一个元素的地址的下一个地址。而且size()函数返回的是已经使用的空间大小,capacity()函数返回的是系统分配的整体空间的大小
vector的元素操作
关于vactor容器的元素操作,我们常用的便是push_back(),pop-back(),clear()和insert(),在此基础上,还要有一个用于清除元素的操作erase(),其中clear()函数也是调用的erase()函数。针对以上的四个函数实现如下。
1.push_back()函数实现源码:
//push_back函数主要用于在容器的尾部插入元素
void push_back()(const T& x){if(finish != end_of_storage){ //存在多余空间construct(finish,x); //参见《STL源码刨析:空间配置器(allocator)》,配置器初始化++finish;}elseinsert_aux(end(),x);
}template <class T, class Alloc>
void vector<T,Alloc>::insert_aux(iterator position, const T& x){if(finish != end_of_storage){ //存在多余空间construct(finish,*(finish - 1));++finish; //调整尾指针T x_copy = x;copy_bcakward(position,finish - 2,finish - 1);*position = x_copy;}else{ //无多余空间const size_type old_size = size(); //当前使用空间const size_type len = ild_size != 0 ? 2 * old_size : 1;//当前空间为0则分配一个内存大小,当前空间不为0则分配2倍的当前空间的大小iterator new_start = data_allocator::allocate(len);iterator new_finish = new_statr;try{ //尝试将先前的元素拷贝到新申请的空间new_finish = uninitialized_copy(start,positon,new_start);//将原容器中的元素插入新容器construct(new_finish,x);++new_finish;new_finish = uninitialized_copy(position,finish,new_finish);//将新元素插入新容器 }catch(){//捕获异常destroy(new_start,new_finish); //释放分配的空间data_allocator::deallocate(new_start,len);throw;}destory(begin(),end()); //释放原来容器的空间deallocate();//更新迭代器start = new_start;finish = new_finish;end_of_storage = new_start + len; }
}
2.pop_back()函数实现源码:
//pop_back()函数主要用于将容器尾部的元素删除
void pop_back(){--finish;destroy(finish); //使用配置器释放空间
}
3.erase()函数实现源码:
//erase()函数主要用于清除容器中的指定范围的元素或指定元素
iterator erase(iterator first, iterator last){ //清除容器中[first,last)范围中的元素iterator i = copy(last,finish,first); //将last后的元素复制到firstdestroy(i,finish); //释放空间finish = finish - (last - first);return finish;
}iterator erase(iterator position){ //清除指定位置的元素if(position + 1 != end())copy(position + 1, finish, position);--finish;destory(finish);return position;
}
针对erase()函数,可参考下图,直观了解其实现过程:

图2.erase()函数的调用过程
4.clear()函数实现源码:
//clear()函数主要用于将容器的元素删除
void clear() { erase(begin(),end()); }
5.insert()函数实现源码:
//insert()函数主要用于将容器的元素插入
template<class T, class Alloc>
void vector<T, Alloc>::insert(iterator position, size_type n, const T& x){if(n != 0){ //插入的元素个数不为0if(size_type(end_of_storage - finish) >= n){ //空闲空间大于插入个数T x_copy = x;const size_type elems_after = finish - position; //获取当前位于插入位置后的元素个数iterator old_finish = finish;if(elems_after > n){ //插入点后的元素个数大于插入的元素个数uninitialized_copy(finish - n, finish, finish); //将插入点后的元素后移finish += n;copy_backward(position, old_finish - n, old_finish);fill(position,position + n,x_copy); //从插入点填充新的元素}else{ //插入点后的元素个数小于等于插入的元素个数uninitialized_fill_n(finih, n - elems_after, x_copy);//将多出的插入元素填充到空闲内存中finish += n;uninitialized_copy(position, old_finish, finish); //将插入点后的元素后移finish += elems_after;fill(position, old_finish, x_copy); //将插入的元素填充至插入点}}else{const size_type old_size = size(); //当前容器大小const size_type len = old_size + max(old_size,n);//计算新的容器大小iterator new_start = data_allocator::allocate(len);iterator new_finish = mew_start;_STL_TRY{ //尝试将元素移动至新的空间new_finish = uninitialized_copy(start,position,new_stars);//将插入点前的当前容器的元素复制到新的空间new_finish = uninitialized_fill_n(new_finish,n,x);//将插入的元素填充至新的空间new_finish = uninitialized_copy(position,finish,new_finish);//将插入点后的当前容器的元素复制到新的空间}#ifdef _STL_USE_EXCEPTIONScatch(...){ //捕获异常destroy(new_stzrt,new_finish);data_allocator::deallocate(ner_start,len);throw;}#endif /*_STL+USE_EXCEPTIONS*/ //无异常destroy(start,finish);deallocate();start = new_start;finish = new_finish;end_of_atorage = new_start + len;}}
}
以上便是本章的内容,针对insert()函数,个人觉得关键点在于插入的元素的个数,如果插入的元素个数大于当前插入点到尾指针的元素个数,则向把旧元素分配至新的空间内再插入新的元素。如果插入的元素个数小于等于当前插入点到尾指针的元素个数,则先将要插入的多出来的元素填充至尾指针后多余的空间内,再将旧元素复制到新的空间内,最后吧插入的元素再填充进去
相关文章:
STL源码刨析:序列式容器之vector
目录 1.序列式容器和关联式容器 2.vector的定义和结构 3.vector的构造函数和析构函数的实现 4.vector的数据结构以及实现源码 5.vector的元素操作 前言 本系列将重点对STL中的容器进行讲解,而在容器的分类中,我们将容器分为序列式容器和关联式容器。本章…...
Flutter 中的 AbsorbPointer 小部件:全面指南
Flutter 中的 AbsorbPointer 小部件:全面指南 在Flutter中,AbsorbPointer是一个特殊的小部件,用于吸收(或“吞噬”)所有传递到其子组件的指针事件(如触摸或鼠标点击)。这在某些情况下非常有用&…...
Web开发学习总结
学习路线 Web 全球广域网,也称为万维网(www World Wide Web),能够通过浏览器访问的网站 初识Web前端 Web标准也称为网页标准,由一系列的标准组成,大部分由W3C(World Wide Web Consortium,万维网联盟)负责制定。三个组…...
springboot相关知识集锦----1
一、springboot是什么? springboot是一个用于构建基于spring框架的独立应用程序的框架。它采用自动配置的原则,以减少开发人员在搭建应用方面的时间和精力。同时提升系统的可维护性和可扩展性。 二、springboot的优点 约定优于配置 版本锁定…...
App推广新境界:Xinstall助你轻松突破运营痛点,实现用户快速增长!
在移动互联网时代,App已经成为企业营销不可或缺的一部分。然而,如何有效地推广App,吸引并留住用户,成为了众多企业面临的难题。今天,我们将为您揭秘一款神奇的App推广工具——Xinstall,它将助您轻松突破运营…...
YOLOv10 论文学习
论文链接:https://arxiv.org/pdf/2405.14458 代码链接:https://github.com/THU-MIG/yolov10 解决了什么问题? 实时目标检测是计算机视觉领域的研究焦点,目的是以较低的延迟准确地预测图像中各物体的类别和坐标。它广泛应用于自动…...
[Spring Boot]baomidou 多数据源
文章目录 简述本文涉及代码已开源 项目配置pom引入baomidouyml增加dynamic配置启动类增加注解配置结束 业务调用注解DS()TransactionalDSTransactional自定义数据源注解MySQL2 测试调用查询接口单数据源事务测试多数据源事务如果依然使用Transactional会怎样?测试正…...
Drone+Gitee自动执行构建、测试和发布工作流
拉取Drone:(至于版本,你可以下载最新的) sudo docker pull drone/drone:2 拉取runner: sudo docker pull drone/drone-runner-docker 在Gitee中添加第三方应用: 进入个人主页,点击设置: 往下翻,找到数…...
Unity3D MMORPG 主城角色动画控制与消息触发详解
Unity3D是一款强大的游戏开发引擎,它提供了丰富的功能和工具,使开发者能够轻松创建出高质量的游戏。其中,角色动画控制和消息触发是游戏开发中非常重要的一部分,它们可以让游戏角色表现出更加生动和多样的动作,同时也能…...
【Text2SQL 经典模型】HydraNet
论文:Hybrid Ranking Network for Text-to-SQL ⭐⭐⭐ arXiv:2008.04759 HydraNet 也是利用 PLM 来生成 question 和 table schema 的 representation 并用于生成 SQL,并在 SQLova 和 X-SQL 做了改进,提升了在 WikiSQL 上的表现。 一、Intro…...
Mysql-根据字段名查询字段在哪些表里
SELECT * FROM information_schema.COLUMNS WHERE COLUMN_NAMElabel_name;...
牛逼!50.3K Star!一个自动将屏幕截图转换为代码的开源工具
1、背景 在当今快节奏的软件开发环境中,设计师与开发者之间的协同工作显得尤为重要。然而,理解并准确实现设计稿的意图常常需要耗费大量的时间和沟通成本。为此,开源社区中出现了一个引人注目的项目——screenshot-to-code,它利用…...
八种单例模式
文章目录 1.单例模式基本介绍1.介绍2.单例模式八种方式 2.饿汉式(静态常量,推荐)1.基本步骤1.构造器私有化(防止new)2.类的内部创建对象3.向外暴露一个静态的公共方法 2.代码实现3.优缺点分析 3.饿汉式(静态…...
禅道密码正确但是登录异常处理
禅道密码正确,但是登录提示密码错误的异常处理 排查内容 # 1、服务器异常,存储空间、数据库异常 # 2、服务异常,文件丢失等异常问题定位 # 1、df -h 排查服务器存储空间 # 2、根据my.php排查数据库连接是否正常 # 3、修改my.pho,debugtrue…...
Go微服务: Nacos的搭建和基础API的使用
Nacos 概述 文档:https://nacos.io/docs/latest/what-is-nacos/搭建:https://nacos.io/docs/latest/quickstart/quick-start-docker/有很多种搭建方式,我们这里使用 docker 来搭建 Nacos 的搭建 这里,我们选择单机模式…...
基于Hadoop的城市公共交通大数据时空分析
基于Hadoop的城市公共交通大数据时空分析 “Spatio-temporal Analysis of Urban Public Transportation Big Data based on Hadoop” 完整下载链接:基于Hadoop的城市公共交通大数据时空分析 文章目录 基于Hadoop的城市公共交通大数据时空分析摘要第一章 引言1.1 研究背景1.2 …...
DNS服务的部署与配置(2)
1、dns的安装及开启 dnf install bind.x86_64 -y #安装 #Berkeley Internet Name Domain (BIND) systemctl enable --now named #启用dns服务,服务名称叫named firewall-cmd --permanent --add-servicedns #火墙设置 firewall-cmd --reload …...
MySql--SQL语言
目录 SQl---DDL 结构定义 创建、删除 数据库 代码 运行 设计表 数据类型 整数 浮点数 主键 约束 主键自增长 默认值 字段注释 创建、删除 表 代码 运行 代码 代码 运行 SQL---DML 数据操纵 插入数据 代码 运行 代码 运行 代码 运行 代码 …...
【网络安全】2030年十大新兴网络安全威胁
欧盟网络安全局(ENISA)已发布了一份全面的清单,列出了预计到2030年将影响数字领域的十大新兴网络安全威胁。 该预测是为期八个月的广泛研究的成果,融合了ENISA前瞻专家小组、CSIRTs网络以及欧盟CyCLONe专家的见解。 这项研究突显…...
python数据分析-CO2排放分析
导入所需要的package import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import datetime %matplotlib inline plt.rcParams[font.sans-serif] [KaiTi] #中文 plt.rcParams[axes.unicode_minus] False #负号 数据清洗…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
