C++ :STL中vector扩容机制

vector是STL提供的动态数组,它会在内部空间不够用时动态的调整自身的大小,调整过程中会有大量的数据拷贝,为了减少数据拷贝的次数vector会在调整空间的时候尽量多申请一些空间,这些预留出的空间可以很大程度上减少拷贝的发生。

在windows环境中使用vs运行这段代码

#include<iostream>
#include<vector>
using namespace std;
void fun(vector<int>&vec){vec.push_back(1);cout<<&vec[0]<<vec.size()<<" "<<vec.capacity()<<endl;
}
int main(){vector<int>vec;for(int i=0;i<10;i++) fun();
}

打印内容依次为,首元素地址,已经使用的空间,容器的容量
在这里插入图片描述

首先看push_back源码,第一个拷贝版本,第二个移动版本,需要注意的是这里第二个不是万能引用而是右值引用,下面看emplace_back代码

void push_back(const _Ty& _Val) { // insert element at end, provide strong guaranteeemplace_back(_Val);}void push_back(_Ty&& _Val) { // insert by moving into element at end, provide strong guaranteeemplace_back(_STD move(_Val));}

emplace_back使用了可变参数,并且对参数使用了万能引用,从而使得可以在插入节点时调用对应的构造函数,比如:vector<pair<int,int>>vec;vec.emplace_back(1,2);这么做可以很大程度上简化代码的书写,同时还可以减少一次移动或是拷贝的过程,decltype(auto)这个没什么说的根据返回值推导返回值类型。
回归正题,执行emplace_back的时候会判断容量是否充足,size!=capacity的时候会直接加入元素,否则的话会调用_Emplace_reallocate函数进行扩容。

template <class... _Valty>decltype(auto) emplace_back(_Valty&&... _Val) {// insert by perfectly forwarding into element at end, provide strong guaranteeauto& _My_data   = _Mypair._Myval2;pointer& _Mylast = _My_data._Mylast;if (_Mylast != _My_data._Myend) {return _Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...);}_Ty& _Result = *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...);
#if _HAS_CXX17return _Result;
#else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv(void) _Result;
#endif // _HAS_CXX17}

下面看扩容代码,代码有点长我直接在代码上加注释了

template <class... _Valty>pointer _Emplace_reallocate(const pointer _Whereptr, _Valty&&... _Val) {// reallocate and insert by perfectly forwarding _Val at _Whereptr_Alty& _Al        = _Getal();auto& _My_data    = _Mypair._Myval2;//使用的长度pointer& _Myfirst = _My_data._Myfirst;//容器开始的位置pointer& _Mylast  = _My_data._Mylast;//容器末尾_STL_INTERNAL_CHECK(_Mylast == _My_data._Myend); // check that we have no unused capacityconst auto _Whereoff = static_cast<size_type>(_Whereptr - _Myfirst);//插入元素的位置,这个函数insert也有在用,所以插入的位置不一定在尾部const auto _Oldsize  = static_cast<size_type>(_Mylast - _Myfirst);//容器大小if (_Oldsize == max_size()) {//长度超过数组的最大长度时报错_Xlength();}const size_type _Newsize     = _Oldsize + 1;const size_type _Newcapacity = _Calculate_growth(_Newsize);//调用扩容函数const pointer _Newvec           = _Al.allocate(_Newcapacity);//申请新的空间const pointer _Constructed_last = _Newvec + _Whereoff + 1;//计算新的末尾pointer _Constructed_first      = _Constructed_last;_TRY_BEGIN_Alty_traits::construct(_Al, _Unfancy(_Newvec + _Whereoff), _STD forward<_Valty>(_Val)...);//在新的空间添加新的元素_Constructed_first = _Newvec + _Whereoff;//下面是根据插入元素的位置判断如何拷贝if (_Whereptr == _Mylast) { // at back, provide strong guarantee_Umove_if_noexcept(_Myfirst, _Mylast, _Newvec);//移动,不能移动时拷贝} else { // provide basic guarantee_Umove(_Myfirst, _Whereptr, _Newvec);_Constructed_first = _Newvec;_Umove(_Whereptr, _Mylast, _Newvec + _Whereoff + 1);}_CATCH_ALL//拷贝出现异常的时候释放对应的内容_Destroy(_Constructed_first, _Constructed_last);_Al.deallocate(_Newvec, _Newcapacity);_RERAISE;_CATCH_END_Change_array(_Newvec, _Newsize, _Newcapacity);//更新数组信息return _Newvec + _Whereoff;//偏移到新元素的地址}

下面看扩展策略,传入的_Newsize是_Oldsize + 1,_Max是容器最大容量一般是达不到的我试着输出了一下我的环境下是4611686018427387903,可以看到正常情况下新的容量是以前的容量的1.5倍(不同编译器的扩容策略不一样,g++中是2),当新的容量还不够的时候会转而按需分配。

size_type _Calculate_growth(const size_type _Newsize) const {// given _Oldcapacity and _Newsize, calculate geometric growthconst size_type _Oldcapacity = capacity();const auto _Max              = max_size();if (_Oldcapacity > _Max - _Oldcapacity / 2) {return _Max; // geometric growth would overflow}const size_type _Geometric = _Oldcapacity + _Oldcapacity / 2;if (_Geometric < _Newsize) {return _Newsize; // geometric growth would be insufficient}return _Geometric; // geometric growth is sufficient}

需要注意的是resize申请机制是按需分配的,当新的容量大于旧的时候会更新容量大小,当新的大小大于旧的容量的时候只会删除多余的元素而不会进行拷贝,数组容量不变不会释放数组空间但是多余的元素会被析构掉,详情运行下面这段代码。

#include<iostream>
#include<vector>
using namespace std;class A {
public:A(){}int* a = new int(1);~A() { cout << this << "析构"<<endl; }
};void fun(vector<A>& vec) {vec.push_back(A());cout << &vec[0] << " " << vec.size() << " " << vec.capacity() << endl;
}
int main() {vector<A>vec;for (int i = 0; i < 10; i++) fun(vec);vec.resize(5);cout << vec.capacity()<<endl;
}

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

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

相关文章

适合新手小白的wordpress详细安装教程

1、下载程序 到wordpress官方网站下载wordpress程序&#xff0c;官方下载地址&#xff1a;Download | WordPress.org China 简体中文。 下载最新版的wordpress程序 https://cn.wordpress.org/latest-zh_CN.zip 2、上传程序 上传程序前先确认主机是否符合安装的环境要求&…

文件压缩与解压命令

(1)tar 打包或解包 tar 将文件打包tar cvf xxxx.tar(打包好的名字) 需要打包的所有文件名 各参数的意义如下: c:创建包文件 f:指定目标为文件而不是设备; v:显示详细过程; t:显示包中的内容而不释放; x:释放包中的内容 z:GNU版本新加的,使得tar有压缩和解压的功能; 解…

爬虫基础训练题

1.抓取imooc网站实战课程部分的课程名称&#xff08;所有课程大概7页&#xff0c;抓取1到5页&#xff09;&#xff0c;并把所有课程名称存储为txt文件第一页地址 2.设置一个请求头&#xff08;headers&#xff09;&#xff0c;这是一个字典&#xff0c;用于在HTTP请求中设置请…

wordpress搬家修改前缀后台无法访问

wordpress更换域名后&#xff0c;后台不能访问&#xff0c;出现以下提示&#xff1a;Sorry, you are not allowed to access this page. 出现这种情况一般是因为更改了数据表的前缀了&#xff0c;比如&#xff0c;由原来的wp_改为了wp_louyuwu_ 解决办法&#xff1a; 1、打开…

【C++】 switch 语句case后面可以跟 string变量吗? case后面可以怎么跟 string变量?

文章目录 在 C 中&#xff0c;case 语句通常是用于 switch 语句中&#xff0c;用来匹配整数或枚举类型的常量。在标准的 C 中&#xff0c;case 语句后面不能直接跟字符串变量&#xff0c;因为 case 语句要求其后面是一个常量表达式。 如果想在 switch 语句中匹配字符串&#x…

职场人必备!效率翻倍的多微信号必备管理工具大揭秘

在职场中&#xff0c;高效率的工作方式是非常重要的。而为了提高工作效率&#xff0c;合理运用一些工作神器也是必不可少的。今天给大家分享一个多微信号管理工具——微信管理系统&#xff0c;它能够帮助职场人员管理多个微信号&#xff0c;让工作变得更加高效。 首先&#xf…

mamba的学习记录

最近新出了一种很火的架构mamba&#xff0c;听说吊打transformer&#xff0c;特此学习一下&#xff0c;总结一下学习的内容。 state-spaces/mamba (github.com)3个月8Kstar&#xff0c;确实有点受欢迎。 目录 1.先验 RNN​ LSTM ​2.mamba State Space Models​ Selecti…

Django 自定义中间件(IP限制频率、用户权限)

【一】用户权限 &#xff08;1&#xff09;要求 不同的用户&#xff0c;有不同的操作权限比如 超级管理员&#xff0c;拥有所有权限管理员可以增加、查询、修改普通用户只能查询 &#xff08;2&#xff09;逻辑梳理 理论来说&#xff0c;这个权限需要保存在数据库中 但是这…

React和Vue.js的有什么区别

在当今前端开发领域&#xff0c;React 和 Vue.js 作为两大热门的前端框架备受开发者关注。它们各自拥有独特的特点和优势&#xff0c;在实际项目中有着广泛的运用。本文将深入探讨 React 和 Vue.js 之间的区别&#xff0c;从组件化方式、数据绑定、模板语法以及生态系统和工具支…

案例 | 华院计算x第一财经:我和我的数智人唱双簧

创新关乎命运&#xff0c;科技引领未来。生成式人工智能(AIGC)给传媒行业发展带来严峻挑战的同时&#xff0c;也带来千载难逢的重大发展机遇。2024年政府工作报告中提出&#xff0c;要深化大数据、人工智能等研发应用&#xff0c;开展“人工智能”行动&#xff0c;打造具有国际…

俚语加密漫谈

俚语加密是一种古老而有效的通信方式&#xff0c;将特定词语或短语在群体内赋予特殊含义&#xff0c;从而隐藏真实信息。类似于方言&#xff0c;它在历史上的应用不可忽视。随着计算机时代的到来&#xff0c;现代密码学通过数学运算编织密语&#xff0c;使得加密变得更加高深莫…

【win10 win11添加右键】git bash

打开注册表编辑器。 按下Win键 R&#xff0c;然后输入”regedit”并按下回车键来打开注册表编辑器。计算机\HKEY_CLASSES_ROOT\Directory\Background\shell\git_bash\command2. 导航到注册表路径&#xff1a;依次展开”HKEY_CLASSES_ROOT\Directory\Background\shell”。右键…

【数据结构】 HashMap源码分析(常量+构造方法+方法)

文章目录 HashMap源码分析一、成员常量二、构造方法三、方法1.此时假定为进行了无参构造&#xff0c;没有分配内存2.当发生有参构造时&#xff0c;完成对容量的大小判断后&#xff0c;将容量大小&#xff0c;传进tableSizeFor方法中&#xff1a; HashMap源码分析 一、成员常量…

iptables笔记

简介&#xff1a; iptables的前身叫ipfirewall &#xff08;内核1.x时代&#xff0c;当内核发展到2.x系列的时候&#xff0c;软件更名为ipchains&#xff0c;现在更名iptables&#xff0c;可以将规则组成一个列表&#xff0c;实现绝对详细的访问控制功能。 工作在用户空间中&…

如何使用Docker轻松构建和管理应用程序(二)

上一篇文章介绍了 Docker 基本概念&#xff0c;其中镜像、容器和 Dockerfile 。我们使用 Dockerfile 定义镜像&#xff0c;依赖镜像来运行容器&#xff0c;因此 Dockerfile 是镜像和容器的关键&#xff0c;Dockerfile 可以非常容易的定义镜像内容&#xff0c;同时在我们后期的微…

JDK8中ArrayList扩容机制

前言 这是基于JDK8的源码分析&#xff0c;在JDK6之前以及JDK11之后细节均有变动&#xff01;&#xff01; 首先来看ArrayList的构造方法 public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Seriali…

VUE3——Proxy API 与VUE2——defineProperty API区别

一、Object.defineProperty 定义&#xff1a;Object.defineProperty() 方法会直接在一个对象上定义一个新属性&#xff0c;或者修改一个对象的现有属性&#xff0c;并返回此对象 为什么能实现响应式 通过defineProperty 两个属性&#xff0c;get及set get 属性的 getter 函…

LeetCode刷题---查询近30天活跃用户数

1.给出满足的条件&#xff0c;截止至2019-07-27的近30天 activity_date BETWEEN DATE_ADD(2019-07-27,INTERVAL -29 day) and 2019-07-27这里使用了Between and 函数和 Date_add函数 2.按照日期分组&#xff0c;统计活跃用户个数 select activity_date day,count(distinct(us…

Python学习之-基础语法

第1关&#xff1a;行与缩进 任务描述 本关任务&#xff1a;改正代码中不正确的缩进&#xff0c;使其能够正常编译&#xff0c;并输出正确的结果。 编程要求 根据提示&#xff0c;改正右侧编辑器中代码的缩进错误&#xff0c;使其能够正确运行&#xff0c;并输出结果。 测试说明…

AcWing 1215. 小朋友排队 (树状数组)

n n n 个小朋友站成一排。 现在要把他们按身高从低到高的顺序排列&#xff0c;但是每次只能交换位置相邻的两个小朋友。 每个小朋友都有一个不高兴的程度。 开始的时候&#xff0c;所有小朋友的不高兴程度都是 0 0 0。 如果某个小朋友第一次被要求交换&#xff0c;则他的不…