C++源码剖析——vector和array
  前言:之前看过侯老师的《STL源码剖析》但是那已经是多年以前的,现在工作中有时候查问题和崩溃都需要了解实际工作中使用到的STL的实现。因此计划把STL的源码再过一遍。
   摘要:本文描述了llvm中libcxx的std::vector的实现。
   关键字:vector
   其他:参考代码LLVM-libcxx
  vector是标准库中的连续存储的容器,也就是容器中说任意两个索引上相邻的元素的地址也是相邻的,可以通过索引随机访问。vector中的元素默认是通过堆内存管理的,在进行空间分配时一般会比时机需求的空间要大,即capacity_size,这样能够避免在插入元素时频繁申请内存导致的性能问题(如果频繁申请内存导致页置换的话还是很耗时的)。
1 vector
  先看下容器的定义,和其他容器一样都是一个模板类。_Tp就是类型,而_Allocator是进行内存管理的分配器,默认分配器就是通过::operator new和::operator delete申请和释放内存的。
template <class _Tp, class _Allocator /* = allocator<_Tp> */>
class _LIBCPP_TEMPLATE_VIS vector
  vector的内存布局比较简单如下图,有三个指针分别指向了对应的开始地址,已使用部分的尾地址,申请到的内存的尾地址,[__begin_, __end_)之间是已经使用的内存部分,[__end_, __end_cap_)是申请了但是未使用的部分(保留这一部分是为了避免插入元素时频繁allocate而可能出现的性能问题。)
 
private:pointer __begin_ = nullptr;pointer __end_ = nullptr;__compressed_pair<pointer, allocator_type> __end_cap_ =__compressed_pair<pointer, allocator_type>(nullptr, __default_init_tag());
构造和销毁
   vector的构造比较简单,就是先通过allocator申请一块内存,然后通过for循环逐个构造对象。构造时通过for循环实现,由于没有利用CPU的一些SMID指令的优化,必然效率不是很好。
vector(size_type __n, const value_type& __x, const allocator_type& __a) : __end_cap_(nullptr, __a){std::__debug_db_insert_c(this);if (__n > 0){__vallocate(__n);__construct_at_end(__n, __x);}
}vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x){_ConstructTransaction __tx(*this, __n);const_pointer __new_end = __tx.__new_end_;for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {__alloc_traits::construct(this->__alloc(), std::__to_address(__pos), __x);}
}
  销毁就比较直接,通过一个包装器__destroy_vector,先clear再调用deallocate释放内存。
__vec_.__clear();
__alloc_traits::deallocate(__vec_.__alloc(), __vec_.__begin_, __vec_.capacity());;
clear
   clear函数只会针对析构容器中的每一个函数并不会释放当前容器中的内存。因此在进行容器释放时需要注意,如果期望释放内存的话可以通过vector().swap(vec)的方式或者在调用clear之后调用shrink_to_fit 调整内存大小。
void clear() _NOEXCEPT{size_type __old_size = size();__clear();__annotate_shrink(__old_size);  //看源码里面什么也不会做std::__debug_db_invalidate_all(this);
}void __clear() _NOEXCEPT {__base_destruct_at_end(this->__begin_);}
void __base_destruct_at_end(pointer __new_last) _NOEXCEPT {pointer __soon_to_be_end = this->__end_;while (__new_last != __soon_to_be_end)          //依然是一个完整的循环析构__alloc_traits::destroy(__alloc(), std::__to_address(--__soon_to_be_end));this->__end_ = __new_last;
}
push_back
   push_back时,如果当前有足够的的大小则会在尾部构建一个对象,扩容的大小是按照现有大小的2倍来,即std::min(max_size(), std::max(current_cap + 1, 2 * current_cap)),简单的理解就是在条件允许的情况下扩容2倍。
void vector<_Tp, _Allocator>::__push_back_slow_path(_Up&& __x){allocator_type& __a = this->__alloc();//__split_buffer就是一个包装器__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), size(), __a);// __v.push_back(std::forward<_Up>(__x));__alloc_traits::construct(__a, std::__to_address(__v.__end_), std::forward<_Up>(__x));__v.__end_++;__swap_out_circular_buffer(__v);//这个函数没有干什么就是将__v中的size设置给当前的vector
}
emplace_back
   emplace_back和push_back基本相同都是向容器中插入元素,如果对于插入vector::value_type类型的对象二者是没有区别的,push_back也实现了右值的重载,不存在push_back对于右值会多次拷贝的情况。主要的区别是emplace_back通过可变参数模板将参数直接传递给了构建器也就意味着同样的代码emplace_back直接在对应的内存上构建对象,而相比之下push_back是先构建再拷贝。
void vector<_Tp, _Allocator>::emplace_back(_Args&&... __args)
resize
   resize的实现比较直接,内存小了就扩容,大了就析构但是并不释放内存。
void vector<_Tp, _Allocator>::resize(size_type __sz, const_reference __x){size_type __cs = size();if (__cs < __sz)this->__append(__sz - __cs, __x);else if (__cs > __sz)this->__destruct_at_end(this->__begin_ + __sz);//只会析构对象,不会释放内存
}
shrink_to_fit
   
vector<bool, _Allocator>::shrink_to_fit() _NOEXCEPT{if (__external_cap_to_internal(size()) > __cap()){vector(*this, allocator_type(__alloc())).swap(*this);}
}
vector<bool>基本上被建议放弃使用了,所以就不深入了。
2 array
  array的实现比较简单就是一个简单的栈数组的包装器。就不详细描述了。
template <class _Tp, size_t _Size>
struct _LIBCPP_TEMPLATE_VIS array
{// types:typedef array __self;typedef _Tp                                   value_type;typedef value_type&                           reference;typedef const value_type&                     const_reference;typedef value_type*                           iterator;typedef const value_type*                     const_iterator;typedef value_type*                           pointer;typedef const value_type*                     const_pointer;typedef size_t                                size_type;typedef ptrdiff_t                             difference_type;typedef _VSTD::reverse_iterator<iterator>       reverse_iterator;typedef _VSTD::reverse_iterator<const_iterator> const_reverse_iterator;_Tp __elems_[_Size];
};
3 参考文献
- stackoverflow——push_back vs emplace_back
- Proposed Wording for Placement Insert
相关文章:
 
C++源码剖析——vector和array
前言:之前看过侯老师的《STL源码剖析》但是那已经是多年以前的,现在工作中有时候查问题和崩溃都需要了解实际工作中使用到的STL的实现。因此计划把STL的源码再过一遍。 摘要:本文描述了llvm中libcxx的std::vector的实现。 关键字&…...
 
学习linux编程(一)
本文导航一. Linux基础知识杂记0. terminal操作快捷键等1. 为什么vfork的子进程里用return,整个程序会挂掉,而且exit不会(zz)2. 进程内存管理详解3. 关于堆和自由存储区概念的区别4. cache和buffer的区别5. C实现线程池6. 静态函数和虚函数的区别7. C里是…...
pt-query-digest_详细使用方法
pt-query-digest_详细使用方法1. pt介绍1.1. 说明1.2. 安装2 语法选项2.1 所有参数2.2 常见参数2.3 事件和属性2.4 分组2.5 过滤2.6 排序2.7 输出选项2.8 DSN(数据源)选项3. 慢日志3.1 事件属性3.2 分析报告3.2.1 第一部分:总体概况说明3.2.2 第二部分:查…...
基于MATLAB编程的萤火虫FA优化BP神经网络的回归分析
目录 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络激活函数及公式 SVM应用实例,基于fa-svm分类预测 代码 结果分析 展望 BP神经网络的原理 BP神经网络的定义 人工神经网络无需事先确定输入输出之间映射关系的数学方程,仅通过…...
leetcode 消失的数字(面试题)
题目 数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗? 注意:本题相对书上原题稍作改动 示例 1: 输入:[3,0,1] 输出:2 示例 2: 输入&…...
 
Spring入门篇6 --- AOP
目录1.核心概念AOP(Aspect Oriented Programming)面向切面编程:一种编程范式,指导开发者如何组织程序结构作用:在不惊动原始设计的基础上为其进行功能增强。连接点(JoinPoint):程序执行过程中的任意位置切入点(PointCut)ÿ…...
 
linux 配置java环境
1、上传jdk包到/usr/local/java目录下 2、解压jdk的tar包 tar -zxvf jdk-8u291-linux-x64.tar.gz 3、添加配置(环境变量) 注意:JAVA_HOME值为实际jdk路径 打开配置文件 vi /etc/profile 最下面添加: #set java environment JAVA_HOME/usr/…...
 
分布式事务基础入门
分布式事务基础入门 什么是分布式事务 什么是分布式事务? 首先理解什么是本地事务? 平常我们在程序中通过spring去控制事务是利用数据库本身的事务特性来实现的,因此叫数据库事务,由于应用主要靠关系数据库来控制事务࿰…...
 
白盒测试究竟怎么做
大家好,我是洋子 在进行日常测试的时候,我们大部分时间花在手动的功能测试上,功能测试又可称为手工测试,官方一点的学名叫黑盒测试,当然作为测试工程师,我们一般俗称点点点 黑盒测试是一种软件测试方法&am…...
 
EEG微状态的功能意义
导读大脑的瞬时全局功能状态反映在其电场结构上。聚类分析方法一致地提取了四种头表面脑电场结构,这些结构能够最佳地解释自发EEG记录中随时间变化的差异。这四种结构被称为EEG微状态A、B、C和D类,分别与言语/语音、视觉、主观感受-自主加工和注意力重定…...
Python3 - Flask+swift实现单点登录
基于 Flask 和 Redis 实现单设备登录的服务端代码和客户端swift、oc代码: Python flask 实现服务端 from flask import Flask, jsonify, request from redis import Redisapp Flask(__name__) redis_db Redis()# 用户登录接口,验证用户名和密码&#…...
 
HTML URL
文章目录HTML URLURL - 统一资源定位器常见的 URL SchemeURL 字符编码URL 编码实例HTML URL URL 是一个网页地址。 URL可以由字母组成,如"csdn.net",或互联网协议(IP)地址: 192.168.100.1。大多数人进入网站…...
带你了解ICCV、ECCV、CVPR三大国际会议
文章目录 前言 一、ICCV、ECCV、CVPR是什么? 1.ICCV 2.ECCV 3.CVPR 二、三大会链接及论文下载链接 前言 作为刚入门CV的新人,有必要记住计算机视觉方面的三大顶级会议:ICCV,CVPR,ECCV,统称为ICE。 与其它学术领域不同,计算机科学使用会议而不是期刊作为发表研究成果的主…...
 
常用的一些代码
今天菜鸟涨工资了,到手估计有8000左右了,有点开心,本来想上一篇就把这篇写了的,但是发现还是分开写比较好! 文章目录自适应js禁止放大播放声音store的使用websocket封装echarts实现渐变swiper常用的陌生方法࿰…...
Python-df.pop()和np.array.shape()属性
1.df.pop() 删除某一列 可以使用这个来删除某一列(不能是多列),只有一个参数,就是列名,可以是str类型,函数返回的是被删除的列,df直接是删除后的df,不需要我们处理。 我们建模时&a…...
 
多线程并发编程笔记03(小滴课堂)---线程安全性
原子性操作: 这样一段代码。 我们输出一下: 我们发现它的结果和我们想的不太一样。 正常应该输出1000. 这是因为没有保证原子性。 所以我们来加上原子性: 这样就保证了我们的原子性。 接下来我们来细说说这个关键字: 我发现我…...
 
提升代码质量,使用插件对 java 代码进行扫描检查分析
目录前言一、使用maven-checkstyle-plugin插件1. maven-checkstyle-plugin 介绍2.引入依赖3.使用二、使用 idea 插件1.安装2.使用前言 很多时候我们的代码写的不规范,比如没缩进、参数间没空格、导入的包没用到没删除、方法很长没有进行拆分、 直接对方法参数进行了…...
如何用秒验提升用户体验和转换率?
手机号验证是移动应用开发中常见的需求,它可以用于用户注册、登录、身份认证等场景。目前,市场上主要的手机号验证方式是短信验证码,但这种方式存在一些问题,例如: 延迟:短信验证码需要等待运营商发送和用…...
 
【新】(2023Q2模拟题JAVA)华为OD机试 - 机器人活动区域
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:机器人活动区域 题目 现有一…...
 
2023软件测试面试真题宝典大汇总,没收藏的都后悔了
下边是我根据工作这几年来的面试经验,加上之前收集的资料,整理出来350道软件测试工程师 常考的面试题。字节跳动、阿里、腾讯、百度、快手、美团等大厂常考的面试题,在文章里面都有提到。 虽然这篇文章很长,但是绝对值得你点击一…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
 
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
 
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
 
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
 
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
 
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
 
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
