最近,被面试官的一道题问倒,很失落,明明看过《STL源码分析》,为啥这种问题还没答好,只能说自己看的时候没有仔细去思考。这道题就是标题的问题,面试完我重新看了一遍《STL源码分析》中关于这块的内容,这里记录下自己看完的一点理解。

在STL中,一般对容器的内存分配和构造是分开的2个过程,STL有专门的空间配置器负责分配内存,而构造则是通过placement new在已申请的内存上进行的,vector也不除外,下面是vector的push_back函数源码:

template <class T, class Alloc = alloc>
void vector::push_back(const T& x)
{if (finish != end_of_storage){construct(finish, x);++finish;}else{insert_aux(finish, x);}
}

其中,construct是STL的全局函数,是所有容器共用的,它的具体实现如下:

template<class T1, class T2>
inline void construct(T1* p, const T2& value)
{new(p) T1(value);    //placement new, 调用T1::T1(value);
}

而insert_aux是vector自己的成员函数,具体实现如下:

template <class T, class Alloc = alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x)
{//还有备用空间if (finish != end_of_storage){//在备用空间起始处构造一个元素,并以vector最后一个值为其初值construct(finish, *(finish - 1));//调整水位++finish;//拷贝一个元素T copy_x = x;//把vector插入位置position之后的元素往后移一位copy_backward(position, finish - 2, finish - 1);//给position指向的地方赋值,赋值内容为前面拷贝的元素*position = copy_x;}//已无备用空间else{const size_type old_size = size();//如果原来的vector为空,则申请一个元素的空间,否则申请可以容纳原来2倍元素的空间const size_type len = old_size == 0 ? 1 : 2 * old_size;//全局函数,申请空间iterator new_start = data_allocator::allocate(len);iterator new_finish = new_start;try{//将原来vector的position之前的内容拷贝到新的vector前面new_finish = uninitialized_copy(start, position, new_start);//调用构造函数为新插入的元素赋值construct(new_finish, x);//调整水位++new_finish;//将原来vector的postion之后的内容拷贝到新的vector后面new_finish = uninitialized_copy(position, finish, new_finish);}catch (...){//析构destroy(new_start, new_finish);//释放刚刚申请的内存data_allocator::deallocate(new_start, len);throw;}//析构原vecotrdestroy(begin(), end());//释放原vector的空间deallocate();//调整迭代器指向新的vectorstart = new_start;finish = new_finish;end_of_storage = new_start +len;}
}

上面空间配置函数allocate的最底层实现比较复杂,但是只要记住2点即可,(1)如果申请的空间大于128字节,就直接调用malloc申请,(2)如果申请的空间小于等于128字节,就从STL维护的16条free list里面寻找合适的一块内存使用(这16条free list各自管理大小分别为8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128字节的小额内存块,之所谓维护这些list既是为了防止频繁调用malloc,也是为了避免太多小额区块造成内存的碎片)。

介绍了这么多,回到最初的问题,如果我们一开始定义了一个空的vector,然后现在要往里面push_back一个个对象,这个过程具体是怎样的,请看下面这段代码的注释,这些都是我重新看了一遍书的理解:

//定义一个类A
class A{int x;double y;
};//定义一个空的vector,vector中可以存放的是类A的对象
vector<A> vec;//定义类A的对象a,对象b,对象c和对象d
A a;
A b;
A c;
A d;/*
注意:根据上面对push_back的源码分析可知,
因为一开始vec是空的,所以会走insert_aux函数的无可用空间的分支,
调用allocate申请一块能够容纳《一》个A类对象的内存,
并调用拷贝构造函数把a赋值给vec的finish迭代器指向的内存,
说白了就是vec中存放的a和上面定义的a对象已经不是一个东西了。
*/
vec.push_back(a);/*
注意:根据上面对push_back的源码分析可知,
现在vec也没有多余的可用空间,所以会再次走insert_aux函数的无可用空间的分支,
调用allocate申请一块能够容纳《两》个A类对象的内存,
把原来的vec的唯一一个元素a移动到新的vec上去,
并调用拷贝构造函数把b赋值给新的vec的finish迭代器指向的内存,这时a和b存放在相邻内存中。
*/
vec.push_back(b);/*
注意:根据上面对push_back的源码分析可知,
现在vec也没有多余的可用空间,所以会再次走insert_aux函数的无可用空间的分支,
调用allocate申请一块能够容纳《四》个A类对象的内存,
把原来的vec的两个元素a和b移动到新的vec上去,
并调用拷贝构造函数把c赋值给新的vec的finish迭代器指向的内存,
这时a和b和c存放在相邻内存中,而且这块内存还能再容纳一个类A对象。
*/
vec.push_back(c);/*注意:根据上面对push_back的源码分析可知,
现在vec还有一个可用空间,所以这次会走construct函数的分支,
通过placement new在已有的内存上调用拷贝构造函数把d赋值给finish迭代器指向的内存,
这时a和b和c和d存放在相邻内存中,但这块内存又再次没有剩余空间了。
*/
vec.push_back(d);

就写到这里吧,继续面壁思过了。

STL之vector的push_back过程详解相关推荐

  1. STL之七:STL各种容器的使用时机详解

    C++标准程序库提供了各具特长的不同容器.现在的问题是:该如何选择最佳的容器类别?下表给出了概述. 但是其中有些描述可能不一定实际.例如:如果你需呀处理的元素数量很少,可以虎落复杂度,因为线性算法通常 ...

  2. python属性使用教程_Python对象的属性访问过程详解

    只想回答一个问题: 当编译器要读取obj.field时, 发生了什么? 看似简单的属性访问, 其过程还蛮曲折的. 总共有以下几个step: 1. 如果obj 本身(一个instance )有这个属性, ...

  3. STL迭代器(iterator)用法详解

    C++ STL迭代器(iterator)用法详解 无论是序列容器还是关联容器,最常做的操作无疑是遍历容器中存储的元素,而实现此操作,多数情况会选用"迭代器(iterator)"来实 ...

  4. 《算法笔记》学习日记——6.1 vector的常见用法详解

    目录 6.1 vector的常见用法详解 问题 A: Course List for Student (25) 问题 B: Student List for Course (25) 小结 6.1 ve ...

  5. Android 7.0 Audio的Resample过程详解

    Android 7.0 Audio的Resample过程详解 Qidi 2017.02.23 (Markdown & Haroopad) [前言] 处理过音频文件的工程师都知道音频数据存在采样 ...

  6. 嵌入式linux的u-boot系统启动过程,【站友投递】U-boot启动过程详解

    [站友投递]U-boot启动过程详解 来源:互联网 作者:denny 时间:2009-03-18 Tag:点击: 一.U-BOOT的目录结构 u-boot目录下有18个子目录,分别存放管理不通的源程序 ...

  7. hadoop作业初始化过程详解(源码分析第三篇)

    (一)概述 我们在上一篇blog已经详细的分析了一个作业从用户输入提交命令到到达JobTracker之前的各个过程.在作业到达JobTracker之后初始化之前,JobTracker会通过submit ...

  8. Hadoop学习之Mapreduce执行过程详解

    一.MapReduce执行过程 MapReduce运行时,首先通过Map读取HDFS中的数据,然后经过拆分,将每个文件中的每行数据分拆成键值对,最后输出作为Reduce的输入,大体执行流程如下图所示: ...

  9. python的执行过程_在交互式环境中执行Python程序过程详解

    前言 相信接触过Python的伙伴们都知道运行Python脚本程序的方式有多种,目前主要的方式有:交互式环境运行.命令行窗口运行.开发工具上运行等,其中在不同的操作平台上还互不相同.今天,小编讲些Py ...

  10. 安卓 linux init.rc,[原创]Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

最新文章

  1. 社交网络图挖掘5--图的邻居性质
  2. NYOJ 116 士兵杀敌(二)
  3. 二十、“安化辞骚千万卒,康吾故土祭吾躯”(2021.6.14)
  4. 机器人学习--Hans Moravec在斯坦福博士论文1980年-Obstacle Avoidance and Navigation in the Real World by a Seeing Ro
  5. sqoop2 java api实现_Sqoop2 Java客户端API指南
  6. Matlab插值方法大全
  7. merlin.acs的使用方法 merlin.acs添加右键菜单
  8. asp.net ReportViewer 设置 rdlc textbox的值
  9. 安全性能两手抓,华为云MySQL“非双一特性”助力企业业务稳定高效运行
  10. c语言从键盘输入求最大值和最小值,从键盘任意输入10个整数,计算并输出最大值和最小值及......
  11. html 签到日历,写一个签到日历
  12. cookie怎样存储数据?
  13. jQuery碎语(2) 事件
  14. python 服务监控_promethues + python + flask监控后端服务状态
  15. 解决wordpress用户注册时,点击邮件中的重置密码链接提示“您的密码重设链接无效”
  16. 微信开放平台授权流程
  17. linux 微信客户端 mojo,QQ微信一体客户端教程
  18. Matplotlib绘制立方体示意图-伪三维
  19. window.print()+layer.open()——实现打印A4纸张内容的功能——功能实现
  20. C语言实现扫描文件下所有目录

热门文章

  1. Linux中的远程连接
  2. [跟进]_微软关闭MSN博客,腾讯第一时间抢占市场
  3. 2020年十七届华为杯数学建模比赛记录
  4. 数字战疫|央视聚焦闵行,有信云助力上海数千家企业复工复产
  5. 用微博图片反查上传者信息
  6. 苹果Mac新手必备技巧|了解使用 Mac 桌面
  7. html添加悬浮图片,HTML5和jQuery制作网页灰度图片悬浮效果_js
  8. 手把手教你批量下载微博视频
  9. 上课笔记-机器学习(4)-泰坦尼克号沉没乘客存活分析
  10. java定义静态常量_如何在Java中定义常量