本文基于STL vector源代码,但是不考虑分配器allocator,迭代器iterator,异常处理try/catch等内容,同时对_Ucopy()、 _Umove()、 _Ufill()函数也不会过度分析。

一、vector的定义

template<class _Ty,class _Ax>class vector: public _Vector_val<_Ty, _Ax>{   // varying size array of values
public:/********/
protected:pointer _Myfirst;   // pointer to beginning of arraypointer _Mylast;    // pointer to current end of sequencepointer _Myend; // pointer to end of array};

简单理解,就是vector是利用上述三个指针来表示的,基本示意图如下:

两个关键大小:
大小:size=_Mylast - _Myfirst;
容量:capacity=_Myend - _Myfirst;
分别对应于resize()、reserve()两个函数。
size表示vector中已有元素的个数,容量表示vector最多可存储的元素的个数;为了降低二次分配时的成本,vector实际配置的大小可能比客户需求的更大一些,以备将来扩充,这就是容量的概念。即capacity>=size,当等于时,容器此时已满,若再要加入新的元素时,就要重新进行内存分配,整个vector的数据都要移动到新内存。二次分配成本较高,在实际操作时,应尽量预留一定空间,避免二次分配。

二、构造与析构

1、构造

vector的构造函数主要有以下几种:

vector() : _Mybase(){   // construct empty vector_Buy(0);}
explicit vector(size_type _Count) : _Mybase(){   // construct from _Count * _Ty()_Construct_n(_Count, _Ty());}
vector(size_type _Count, const _Ty& _Val) : _Mybase(){   // construct from _Count * _Val_Construct_n(_Count, _Val);}
vector(const _Myt& _Right) : _Mybase(_Right._Alval){   // construct by copying _Rightif (_Buy(_Right.size()))_Mylast = _Ucopy(_Right.begin(), _Right.end(), _Myfirst);}

vector优异性能的秘诀之一,就是配置比其所容纳的元素所需更多的内存,一般在使用vector之前,就先预留足够空间,以避免二次分配,这样可以使vector的性能达到最佳。因此元素个数_Count是个远比元素值 _Val重要的参数,因此当构造一个vector时,首要参数一定是元素个数。
由上各构造函数可知,基本上所有构造函数都是基于_Construct _n() 的

bool _Buy(size_type _Capacity){   // allocate array with _Capacity elements_Myfirst = 0, _Mylast = 0, _Myend = 0;if (_Capacity == 0)    //_Count为0时,直接返回return (false);else{   // nonempty array, allocate storage_Myfirst = this->_Alval.allocate(_Capacity);  //分配内存,并更新成员变量_Mylast = _Myfirst;_Myend = _Myfirst + _Capacity;}return (true);}void _Construct_n(size_type _Count, const _Ty& _Val){   // 构造含有_Count个值为_Val的元素的容器if (_Buy(_Count))_Mylast = _Ufill(_Myfirst, _Count, _Val);}

这样就完成了vector容器的构造了。

2、析构

vector的析构函数很简单,就是先销毁所有已存在的元素,然后释放所有内存

void _Tidy(){   // free all storageif (_Myfirst != 0){   // something to free, destroy and deallocate it_Destroy(_Myfirst, _Mylast);this->_Alval.deallocate(_Myfirst, _Myend - _Myfirst);}_Myfirst = 0, _Mylast = 0, _Myend = 0;}

三、插入和删除元素

vector的插入和删除元素是通过push_ back () 、 pop_back()两个接口来实现的,他们的内部实现也非常简单

void push_back(const _Ty& _Val)
{   // insert element at endif (size() < capacity())_Mylast = _Ufill(_Mylast, 1, _Val);elseinsert(end(), _Val);    //空间不足时,就会触发内存的二次分配
}void pop_back()
{   // erase element at endif (!empty()){   // erase last element_Destroy(_Mylast - 1, _Mylast);--_Mylast;}
}

四、其他接口

1、reserve()操作

之前提到过reserve(Count) 函数主要是预留Count大小的空间,对应的是容器的容量,目的是保证(_Myend - _Myfirst)>=Count。只有当空间不足时,才会操作,即重新分配一块内存,将原有元素拷贝到新内存,并销毁原有内存

void reserve(size_type _Count){   // determine new minimum length of allocated storageif (capacity() < _Count){   // not enough room, reallocatepointer _Ptr = this->_Alval.allocate(_Count);_Umove(begin(), end(), _Ptr);size_type _Size = size();if (_Myfirst != 0){   // destroy and deallocate old array_Destroy(_Myfirst, _Mylast);this->_Alval.deallocate(_Myfirst, _Myend - _Myfirst);}_Myend = _Ptr + _Count;_Mylast = _Ptr + _Size;_Myfirst = _Ptr;}}

2、resize()操作

resize(Count) 函数主要是用于改变size的,也就是改变vector的大小,最终改变的是(_Mylast - _Myfirst)的值,当size < Count时,就插入元素,当size >Count时,就擦除元素。

void resize(size_type _Newsize, _Ty _Val){   // determine new length, padding with _Val elements as neededif (size() < _Newsize)_Insert_n(end(), _Newsize - size(), _Val);else if (_Newsize < size())erase(begin() + _Newsize, end());}

3、_Insert_n()操作

resize()操作和insert()操作都会利用到_Insert_n()这个函数,这个函数非常重要,也比其他函数稍微复杂一点
虽然_Insert_n(_where, _Count, _Val ) 函数比较长,但是操作都非常简单,主要可以分为以下几种情况:

1、_Count == 0,不需要插入,直接返回

2、max_size() - size() < _Count,超过系统设置的最大容量,会溢出,造成Xlen()异常

3、_Capacity < size() + _Count,vector的容量不足以插入Count个元素,需要进行二次分配,扩大vector的容量。 在VS下,vector容量会扩大50%,即 _Capacity = _Capacity + _Capacity / 2;若仍不足,则 _Capacity = size() + _Count;

else if (_Capacity < size() + _Count){   // not enough room, reallocate_Capacity = max_size() - _Capacity / 2 < _Capacity? 0 : _Capacity + _Capacity / 2;    // try to grow by 50%if (_Capacity < size() + _Count)_Capacity = size() + _Count;pointer _Newvec = this->_Alval.allocate(_Capacity);pointer _Ptr = _Newvec;_Ptr = _Umove(_Myfirst, _VEC_ITER_BASE(_Where),_Newvec);    // copy prefix_Ptr = _Ufill(_Ptr, _Count, _Val);  // add new stuff_Umove(_VEC_ITER_BASE(_Where), _Mylast, _Ptr);  // copy suffix//内存释放与变量更新}

这种情况下,数据从原始容器移动到新分配内存时是从前到后移动的

4、空间足够,且被插入元素的位置比较靠近_Mylast,即已有元素的尾部

这种情况下不需要再次进行内存分配,且数据是从后往前操作的。首先是将where~last向后移动,为待插入数据预留Count大小的空间,然后从_Mylast处开始填充,然后将从where处开始填充剩余元素

else if ((size_type)(_Mylast - _VEC_ITER_BASE(_Where)) < _Count){   // new stuff spills off end_Umove(_VEC_ITER_BASE(_Where), _Mylast,_VEC_ITER_BASE(_Where) + _Count);   // copy suffix_Ufill(_Mylast, _Count - (_Mylast - _VEC_ITER_BASE(_Where)),_Val);  // insert new stuff off end_Mylast += _Count;std::fill(_VEC_ITER_BASE(_Where), _Mylast - _Count,_Val);  // insert up to old end}

5、空间足够,但插入的位置比较靠前

    {   // new stuff can all be assigned_Ty _Tmp = _Val;    // in case _Val is in sequencepointer _Oldend = _Mylast;_Mylast = _Umove(_Oldend - _Count, _Oldend,_Mylast);   // copy suffix_STDEXT _Unchecked_move_backward(_VEC_ITER_BASE(_Where), _Oldend - _Count,_Oldend);   // copy holestd::fill(_VEC_ITER_BASE(_Where), _VEC_ITER_BASE(_Where) + _Count,_Tmp);  // insert into hole}

4、erase()操作

iterator erase(const_iterator _First_arg,const_iterator _Last_arg){   // erase [_First, _Last)iterator _First = _Make_iter(_First_arg);iterator _Last = _Make_iter(_Last_arg);if (_First != _Last){   // worth doing, copy down over holepointer _Ptr = _STDEXT unchecked_copy(_VEC_ITER_BASE(_Last), _Mylast,_VEC_ITER_BASE(_First));_Destroy(_Ptr, _Mylast);_Mylast = _Ptr;}return (_First);}

主要操作就是将后半部分的有效元素向前拷贝,并将后面空间的无效元素析构,并更新_Mylast变量

5、assign()操作

assign()操作最终都会调用到下面的函数,主要操作是首先擦除容器中已有的全部元素,在从头开始插入Count个Val元素

void _Assign_n(size_type _Count, const _Ty& _Val){   // assign _Count * _Val_Ty _Tmp = _Val;    // in case _Val is in sequenceerase(begin(), end());insert(begin(), _Count, _Tmp);}

五、基本使用

在经过上述对vector内部实现的分析后,再来理解相应接口就变得简单得多。
vector对外接口主要可以分为:

1、构造、析构

vector <Elem> c
vector <Elem> c1(c2)
vector <Elem> c(n)
vector <Elem> c(n, elem)
vector <Elem> c(beg,end)
c.~ vector <Elem>()

2、插入、删除、赋值

c.push_back(elem)
c.pop_back()
c.insert(pos,elem)
c.insert(pos,n,elem)
c.insert(pos,beg,end)
c.erase(pos)
c.erase(beg,end)
c.clear()
c.assign(beg,end)
c.assign(n,elem)

3、大小相关

c.capacity()
c.max_size()
c.resize(num)
c.reserve()
c.size()

4、获取迭代器

c.begin()
c.end()
c.rbegin()
c.rend()

5、获取数据

operator[]
c.at(idx)
c.front()
c.back()

原文链接:https://blog.csdn.net/u012658346/article/details/50725933

C++ vector的内部实现原理及基本用法相关推荐

  1. 前端必读:浏览器内部工作原理

    前端必读:浏览器内部工作原理 作者: Tali Garsiel  发布时间: 2012-02-09 14:32  阅读: 2133 次  原文链接   全屏阅读  [收藏]   http://kb.c ...

  2. WEB HTTP:浏览器HTTP协议漫谈、请求对象Httprequest、响应对象HttpResponse、浏览器内部工作原理(待完善)

    0 系列目录 WEB请求处理 WEB请求处理一:浏览器请求发起处理 WEB请求处理二:Nginx请求反向代理 WEB请求处理三:Servlet容器请求处理 WEB请求处理四:Tomcat配置实践 WE ...

  3. Mininet的内部实现原理简介

    原文发表在我的博客主页,转载请注明出处. 前言 之前模拟仿真网络一直用的是Mininet,包括写了一些关于Mininet安装,和真实网络相连接,Mininet简历拓扑的博客,但是大多数都是局限于具体步 ...

  4. k8s 服务注册与发现(二)Kubernetes内部域名解析原理

    Kubernetes内部域名解析原理 同一集群同一命名空间下 在 Kubernetes 中,比如服务 a 访问服务 b,对于同一个 Namespace下,可以直接在 pod 中,通过 curl b 来 ...

  5. 2021-08-31 CoreDNS:Kubernetes内部域名解析原理、弊端及优化方式

    20211217 更新 ,腾讯云 ipvs模式下 localdns: https://cloud.tencent.com/developer/article/1813985 20210919解决思路: ...

  6. 一颗芯片的内部设计原理和结构

    关注.星标公众号,直达精彩内容 来源:网络素材 摘要 作为一名电源研发工程师,自然经常与各种芯片打交道,可能有的工程师对芯片的内部并不是很了解,不少同学在应用新的芯片时直接翻到Datasheet的应用 ...

  7. 孙鑫VC++LESSON1:Windows程序内部运行原理

    视频链接:https://www.bilibili.com/video/BV1Ts411f7fw/?spm_id_from=333.337.search-card.all.click&vd_s ...

  8. 递归函数内部的原理????不要跟我讲自己调用自己这样的话,我一分也不给你的zz

    楼主wormemory(绿茶)2002-06-13 12:52:41 在 C/C++ / C语言 提问 递归函数内部的原理????不要跟我讲自己调用自己这样的话,我一分也不给你的,     我要知道的 ...

  9. Windows程序内部运行原理

    Windows程序内部运行原理   为了理解Visual C++应用程序开发过程,先要理解Windows程序的运行机制.因为 Visual C++是 Windows 开发语言,需要明白在 Window ...

最新文章

  1. QQ卖手办,用AI分析用户评论
  2. 疑案追踪:Spring Boot内存泄露排查记
  3. Introduction to the Service Provider Interfaces--官方文档
  4. 浙江理工大学2019年1月赛
  5. vs如何写多线程_VS + PS + GS
  6. 离散对数和原根 欧拉定理证明
  7. Dll 导出类 [示例代码]
  8. video thumbnails
  9. java 线程 内存分配内存_漫谈JAVA语言的内存分配
  10. 模式窗体 winform 1201
  11. STM32工作笔记0093---DAC数模转换实验-M3
  12. Chrome development tools学习笔记(3)
  13. java的百度编辑器插件下载安装_ueditor-extend
  14. 解决ojdbc14库引用问题
  15. 因特尔显卡自定义分辨率_Win10创建自定义分辨率的方法教程
  16. 《青年在选择职业时的考虑》——马克思
  17. 如何保存google的壁纸
  18. 数据库课设--基于Python+MySQL的餐厅点餐系统
  19. 实战新浪微博、腾讯微博的分享功能
  20. 电脑端破解之破解注册码

热门文章

  1. python编程入门教学下载-Python编程从入门到实践的PDF教程免费下载
  2. python常用函数-python—各种常用函数及库
  3. python绘制3d图-python中Matplotlib实现绘制3D图的示例代码
  4. python最新版下载教程-各种版本的Python下载安装教程
  5. python工程师-如何面试Python后端工程师?
  6. python自学书籍顺序-2018最全各阶段Python学习书籍汇总!
  7. python基础指令-python的一些基本命令
  8. python安装在什么系统下最好-自学python用什么系统好
  9. javascript高级程序设计之BOM
  10. LeetCode Linked List Cycle II(floyd cycle)