使用reserve来避免不必要的内存重新分配

2023-05-16

STL容器的内存分配策略是,他们会自动增长以便容纳下你放入其中的数据,只要没有超过它的最大限制就可以(要查看最大限制可调用名为max_size的成员函数)。对于vector和string,增长过程是这样来实现的: 每当需要更多空间时,就调用与realloc类似的操作。这一类似realloc的操作分为如下四个步骤:

1.分配一块大小为当前容量的每个倍数的新内存。在大多数实现中,vector和string的容量每次以2的倍数增长,即每当扩容时,他们的容量加倍。

2.把容器的所有元素从旧的内存复制到新的内存中。

3.析构掉旧内存中的对象。

4释放旧内存。

每当发生内存的重新分配时,会带来以下两个问题:

1.效率问题。一系列的分配、释放、复制和析构步骤,必然会带来程序效率的下降。

2.迭代器失效。原来指向旧的容器的迭代器都将失效,因为那块内存已经被释放掉了。

下来我们介绍reserve成员函数。

reserve成员函数能使你把重新分配内存的次数减少到最低,从而避免了重新分配内存带来的问题。在介绍reserve怎么做到这些之前,先来看一下这四个相互关联、但有时会被混淆的成员函数。在标准容器中,只有vector和string提供了所有者四个函数:

1.size()。size()告诉你容器中有多少个元素。它不会告诉你该容器为自己所包含的元素分配了多少内存。

2.capacity()。capacity()告诉你该容器利用已经分配的内存可以容纳多少元素。这是容器所能容纳的元素总数,而不是它还能容纳多少元素。如果你想知道一个vector有多少未被使用的内存,就需要用capacity()-size()。如果capacity和size返回同样的值,就说明容器中不再有剩余空间了,因此下一个插入操作将导致上面讲过的重新分配过程。

3.resize(Container::size_type n)。resize()强迫容器把它的容量改变到包含n个元素的状态。在调用resize之后,size将返回n。如果n比当前的大小(size)要小,则容器尾部的元素将会被析构。如果n比当前的大小要大,则通过默认的构造函数创建的新元素将被添加到容器的尾部。如果n比当前的容量(capacity)要大,那么在添加元素之前,将先重新分配内存。

4.reserve(Container::size_type n)。reserve强迫容器把它的容量变为至少是n,前提是n不小于当前的大小。这通常会导致内存重新分配,因为容量需要增加。如果n比当前容量要小,则vector忽略该调用,什么也不做;而string则可能把自己的容量减为size和n中的最大值。

通过以上概括,应该清楚的是:当一个元素需要被插入容器而容量不够时,就会发生重新分配过程(包括原始内存的分配和释放,对象的拷贝和析构,迭代器、指针和引用的失效)。因此,避免重新分配的关键在于,今早地使用reserve,把容量设置为足够大的值,最好是在容器刚被构造出来之后就使用reserve。

例如,要创建一个包含1-1000之间的值得vector<int>。如果不使用reserve,可能这么写

vector<int> v;
for(int i=1;i<=1000;i++){
    v.push_back(i);
}

对于大多数STL实现,该循环在执行过程中将会导致2到10次重新分配(2的十次方)。把这段改代码改为使用reserve,如下所示 

vector<int> v;
v.reserve(1000);
for(int i=1;i<=1000;i++){
    v.push_back(i);
}

则在循环过程中,就不会发生内存重新分配。

大小(size)和容量(capacity)之间的关系使我们能够预知什么时候插入操作会导致vector或string执行重新分配的动作,进而又使我们有可能预知下一个插入操作什么时候回是容器中的迭代器、指针、和引用失效。例如,考虑下面的代码

string s;
if(s.size() < s.capacity()){
    s.push_back('x');
}

 对push_back的调用不会使string中的迭代器、指针和引用失效,因为string的容量肯定大于它的大小。

总结:通常有两种方式来使用reserve以避免不必要的内存重新分配。第一种方式是,若能确切知道或大致预计容器中最终会有多少元素,则此时可使用reserve。第二种方式是,先预留足够大的空间,然后,当把所有的数据都加入后,再去除多余的容量。如何去除多余的容量,这里有个诀窍,就是使用swap技巧。关于swap技巧的说明,以后会再更新一篇博文专门讲解。

 

参考:《Effective STL》 第14条

 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用reserve来避免不必要的内存重新分配 的相关文章

  • 我有一个IT梦

    介绍 作为一名大二的学生 xff0c 接触计算机基础技术近乎俩年 xff0c 俩年来我愈加发觉计算机是一门发展力很强的学科 它多式多样 xff0c 更像是一种挑战 xff0c 对于好强的我来说 xff0c 越来越着迷计算机的世界 纵然未知的
  • Nginx安装教程

    前言 xff1a 同步文章图片有问题想看带有图片版的请移步 xff1a https www yuque com docs share 3fbd7d5a 639c 4ca8 8500 00071b7cb23d BvpWF 本篇文章涉及ngin
  • Kali-Linux-2020.1 设置中文,汉化。

    Kali Linux 2020 1 设置中文 xff0c 汉化 Kali Linux团队在Twitter上宣布 xff1a 新的一年是进行重大改变的好时机 xff0c 因此 xff0c 我们宣布在 即将发布的2020 1版本中 xff0c
  • PowerShell压缩和解压ZIP文件

    压缩 Compress Archive Path D File DestinationPath E File zip 解压 Expand Archive Path E File zip DestinationPath D File
  • python实现基本算法之归并排序(Merge sort)

    基本算法之归并排序 Merge sort 基本算法 04 归并排序 Merge sort 算法 往期请看选择排序 xff0c 插入排序 xff0c 归并排序 xff0c 快速排序等等都发布的 xff01 欢迎大家批评指正 xff01 文章目
  • mysql 异步复制VS半同步复制

    MySQL数据复制原理 异步复制 xff1a 默认情况下 xff0c MySQL的复制是异步复制 xff0c 主服务器及其从服务器是独立的 异步复制可以提供最佳的性能 xff0c 主服务器将更新的数据写入二进制日志 xff08 Binlog
  • mysql读写分离

    读写分离 master xff1a 192 span class token punctuation span 168 span class token punctuation span 2 span class token punctua
  • 在linux中关闭防火墙

    在linux中关闭防火墙 selinux xff08 secure linux 安全的linux xff09 selinux 是linux下的安全措施机制 xff0c 用来保护linux系统的安全 相当于另外一个安全工具 span clas
  • Google Payments?

    The news broke late last week by way of the Wall Street Journal with rumors of a payments service akin to PayPal forthco
  • 数据库中某个表中的某个字段的值是用逗号隔开的多个值,根据逗号拆分并从另一个表中查出数据返回

    两个表的结构如下 a表 b 表 关系说明 b teacherid 61 a user id 查询思路 xff1a FIND IN SET str strlist xff0c 该函数用于判断 str 是否在 strlist 中 xff0c 如
  • 一个玩游戏的失足青年,转行做游戏开发到教育的挣扎过程

    14年的IT从业经历 xff0c 中专毕业后在小镇上开过网吧 在网吧一年多的时间里 xff0c 天天陪人玩游戏 xff0c 后来去读了一个三流计算机专业 xff0c 毕业后转做软件开发 xff0c 最近五年转入游戏开发行业 xff01 从网
  • minikube的部署和安装,排错

    minikube的部署和安装 安装minikube的步骤 环境准备 xff1a 虚拟机至少2个cpu核心 xff0c 2G内存 xff0c 磁盘20G 推荐使用2个cpu核心 xff0c 4G的内存 xff0c 100G的磁盘空间 前期 x
  • lvm逻辑卷

    lvm示例应用 案例描述 xff1a 增加四块scsi硬盘 xff0c 每块100G xff0c 并构建lvm逻辑卷 xff0c 挂载到相应目录下 传统的分区方式 常用命令 临时添加IP地址 xff1a ip add add 192 168
  • VMware虚拟机开机显示you are in emergency mode 解决办法

    问题描述 xff1a 虚拟机开机出错 xff1a you are in emergency mode 出错原因 xff1a 前不久学习lvm逻辑卷时 xff0c 添加磁盘后 xff0c 修改了 etc fstab文件实现开机自动挂载 因为弄
  • k8s学习笔记-暴露服务--暴露服务踩坑排错

    deployment 部署和调度的工具 xff08 组件 xff09 span class token namespace root 64 k8s2 docker span span class token comment kubectl
  • 容器基本概念 ; kubernetes核心概念

    1 容器基本概念 1 容器与镜像 什么是容器 1 容器是一个视图隔离 xff0c 资源可限制 xff0c 独立文件系统的进程集合 视图隔离 xff1a 如能看见部分进程 xff0c 独立主机名等 控制资源使用率 xff1a 如2G内存大小
  • 阿里云原生学习:02容器的基本概念

    一 容器与镜像 1 1 容器 xff1a 视图隔离 xff0c 资源可限制 xff0c 独立文件系统的进程集合 资源视图隔离 xff08 namespace xff09 如能看见部分进程 xff0c 独立主机名等 控制资源使用率 xff08
  • GO环境配置,goland环境配置

    分别在官网下载go The Go Programming Language google cn IDE goland GoLand A Clever IDE to Go by JetBrains 自行破解 go环境配置 xff1a 添加GO

随机推荐

  • 将goland的terminal配置成shell

    再重启terminal 成功 xff01
  • go命令 build/run/install/env

    1 编译 xff1a go build o 指定生成文件的名字 go build o test exe main go xx go 2 直接运行程序 xff0c 不会编译成exe文件 1 go run go 3 安装程序 go env 4
  • go语言学习

    一 基础语法 1 变量定义 package main import 34 fmt 34 func main 定义变量 xff1a var 常量定义 xff1a const 01 先定义变量 xff0c 再赋值 xff0c var 变量名 数
  • python 检查变量类型_Python检查变量类型– 3种方式

    python 检查变量类型 In this tutorial we are going to learn how to check variable type in Python 在本教程中 xff0c 我们将学习如何在Python中检查变
  • GO学习每日一题

    注 xff1a 每日一题来源go语言官方公众号 day5 package main import 34 fmt 34 下面这段代码能否通过编译 xff0c 原因 答 xff1a 不能 invalid operation sm1 61 61
  • 用go语言实现查找两个数组的异同

    用go语言实现查找两个数组的异同
  • golang 计算本周周一日期

    func GetThisWeekFirstDate thisMonday int64 now 61 time Now offset 61 int time Monday now Weekday if offset gt 0 offset 6
  • Golang 获取今天和昨天零点的时间

    获取今天和昨天零点的时间 func GetTodayAndYesterdayZroe now 61 time Now today 61 time Date now Year now Month now Day 0 0 0 0 time Lo
  • Golang float64转decimal再加减乘除运算,解决float64数据精度问题

    package main import 34 fmt 34 34 github com shopspring decimal 34 func main x 61 decimal NewFromString 34 1 34 y 61 deci
  • Golang float64保留三位小数

    func prettyFloat64 f float64 n int float64 base 61 10 for i 61 1 i lt n i 43 43 base 61 base 10 inst 61 int64 f float64
  • Golang 计算两个时间相差多少分钟

    stime int64 etime int64 时间戳 starttime 61 time Unix stime 0 endtime 61 time Unix etime 0 costtime 61 decimal NewFromFloat
  • Spring自动配置原理

    文章目录 一 概念二 自动配置原理二 自动配置生效总结 一 概念 spring集成其他框架中 xff0c 需要编写大量的xml配置文件 xff0c 编写这些配置文件十分繁琐 xff0c 常常出行错误 xff0c 导致开发效率低 Spring
  • centos 上容器配置X11

    系统 xff1a centos 7 9 连接工具 xff1a 同一个局域网内win10电脑上安装的MobaXterm Personal 步骤 xff1a 找到对应的包 96 yum whatprovides xhost 安装yum y in
  • Java 实现线程安全的方式

    1 创建线程的三种方式 通过实现 Runnable 接口 xff1b 通过继承 Thread 类本身 xff1b 通过 Callable 和 Future 创建线程 2 线程的生命周期 新建状态 使用 new 关键字和 Thread 类或其
  • 生产者消费者问题c语言_C中的生产者消费者问题

    生产者消费者问题c语言 Here you will learn about producer consumer problem in C 在这里 xff0c 您将了解C语言中的生产者消费者问题 Producer consumer probl
  • 2.15多生产者多消费者问题

    视频链接 xff1a https www bilibili com video BV1YE411D7nH p 61 24 一 xff0c 问题描述 桌上有一只盘子 xff0c 每次只能向其中放入一个水果 爸爸向盘子中只放苹果 xff0c 妈
  • 【已解决】xterm: Xt error: Can‘t open display:

    项目场景 xff1a 在MobaXterm中 xff0c 使用Ubuntu 18 04的gdb来debug MPI并行的C 43 43 代码 问题描述 Debug时 xff0c 输入 mpiexec span class token ope
  • mariadb安装

    1 配置官方的mariadb的yum源 手动创建 mariadb repo仓库文件 touch etc yum repos d mariadb repo 然后写入如下内容 mariadb name 61 MariaDB baseurl 61
  • Java 给某段代码加超时时间

    问题原因 xff1a 使用HuTool 的DbTtil 不能设置数据库连接超时时间 xff0c 可能数据库挂了 xff0c 会导致连接一直卡在那 xff0c 也没有异常抛出 xff0c 导致线程一直占着 所以给该段代码加超时时间处理 spa
  • 使用reserve来避免不必要的内存重新分配

    STL容器的内存分配策略是 xff0c 他们会自动增长以便容纳下你放入其中的数据 xff0c 只要没有超过它的最大限制就可以 xff08 要查看最大限制可调用名为max size的成员函数 xff09 对于vector和string xff