string类的erase函数属于stl吗_探索STL容器:vector

用了这么久的 vector ,今天终于有时间来看下STL的实现源码了,开心?~


最近几个月在刷 leetcode ,用的较多的数据结构就是STL里面的 vector 了,相比较于直接的 array 数组,它具备了灵活地根据需求去分配管理内存,用户只管往里面扔东西,拿东西,而不用费心费力去解决C++里面的动态内存问题。

那么大致猜想一下,要实现一个这样的容器,不难想出在 vector 中至少存在这样三个私有成员:head(指向列表第一个元素位置), tail(指向列表最后一个元素位置), size(容器大小)

打开源码看看,得到验证:

template<typename _Tp, typename _Alloc>
struct _Vector_base {
    ......
    struct _Vector_impl_data {
        pointer _M_start;  // 指向容器中的第一个元素,是一个指针,指针类型为 Tp 所示类型
        pointer _M_finish; // 指向容器最后一个元素,也是一个指针
        pointer _M_end_of_storage; // 指向容器最后的位置
        ......
    }
}
template<typename _Tp, typename _Alloc = std::allocator<_tp> >
class vector : protected _Vector_base<_tp> {typedef _Vector_base<_tp> _Base; // 如上 _Vector_base 所示,是一个基础实现
    ......public:typedef _Tp value_type;  // 数据类型typedef typename _Base::pointer pointer; // 全局数据指针
    ......
}

总之就是一层套一层,封装了一个又一个,来完成对数据的抽象。但是最后的接口是放在 vector 上放开的。

vector 支持动态内存分配,支持随机存取访问,因此在数据访问上具备了指针有的特性。那么它在源码上是怎么实现的呢?首先来看它的接口(一部分常用接口)都有哪些:

**注:**虽然包含 头文件就可以用 vector ,但是它的实现源码其实是在 bits/stl_vector.h 下的,而且下面的程序删掉了一些注释类、系统判断类函数。

template<typename _Tp, typename _Alloc = std::allocator<_tp> >
class vector : protected _Vector_base<_tp> {
    ...// 获取指向第一个元素的指针
    iterator begin() { return iterator(this->_M_impl._M_start); }// 获取指向容器最后一个可存放位置的下一个位置iterator end(){ return iterator(this->_M_impl._M_finish); }// 获取容器当前存放了多少元素size_type size() const { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); 
    }// 对容器进行重新分配大小void resize(size_type __new_size) {if (__new_size > size())
            _M_default_append(__new_size - size());else if (__new_size             _M_erase_at_end(this->_M_impl._M_start + __new_size);
    }// 获取容器自身大小size_type capacity() const  {return size_type(this->_M_impl._M_end_of_storage
                         - this->_M_impl._M_start);
    }// 看看容器当前是否为空bool empty() const  { return begin() == end(); }// 获取一个能够对返回对象读写操作的引用类型,其实就是指针
    reference operator[](size_type __n)  {return *(this->_M_impl._M_start + __n);
    }protected:// 检查访问是否合法void _M_range_check(size_type __n) const {if (__n >= this->size())
            __throw_out_of_range_fmt(__N("vector::_M_range_check: __n ""(which is %zu) >= this->size() ""(which is %zu)"),
                                     __n, this->size());
    }public:// 跟 [] 操作符差不多,只不过多了一层范围检查reference at(size_type __n) {
        _M_range_check(__n);return (*this)[__n];
    }// 获取能够对头部元素读写的指针reference front()  { return *begin(); }// 获取最后一个元素reference back()  { return *(end() - 1); }// 向容器末尾追加一个元素进来void push_back(value_type &&__x) { emplace_back(std::move(__x)); }template<typename... _Args>#if __cplusplus > 201402L
    reference#elsevoid#endif
    emplace_back(_Args &&... __args);// 将最后一个元素从容器中删掉void pop_back()  {
        --this->_M_impl._M_finish;
        _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
    }// 在指定位置插入 n 个 __xiterator insert(const_iterator __position, size_type __n, const value_type &__x) {
        difference_type __offset = __position - cbegin();
        _M_fill_insert(begin() + __offset, __n, __x);return begin() + __offset;
    }// 擦除掉某一个位置上的元素iterator erase(const_iterator __position) { return _M_erase(begin() + (__position - cbegin())); 
    }// 交换两个容器中的东西void swap(vector &__x)  {this->_M_impl._M_swap_data(__x._M_impl);
        _Alloc_traits::_S_on_swap(_M_get_Tp_allocator(),
                                  __x._M_get_Tp_allocator());
    }// 将容器内所有元素清空void clear()  { _M_erase_at_end(this->_M_impl._M_start); }
};

程序中大量出现的 allocator ,是来自SGI STL的空间分配器,说白了就是用来分配空间内存的。在拿到这部分源码后,vector 的实现原理也就可以大致有所掌握了(毕竟每个接口也不过几行?,这可能就是优秀设计的结果??)。而在调用过程中无非就是 vector 调用 vector_base 再调用 allocator 再对 _Vector_impl_data 进行操作。

还有一个问题:在添加元素过程中,如果容器满了,那么容器的容量是按照怎样的规则递增呢?参考了《STL源码刨析》之后,得到这样的结论:

如果超过当前容器容量,那么容量会扩增至两倍,如果两倍容量仍不够,那么就扩张至足够大的容量。

在容量的扩张过程中,必须经历“重新分配内存,元素移动,释放原有空间”三个操作,这是因为原有的空间之后不一定能够满足需求,所以统一进行这三个操作来完成。

如何知道是扩增两倍的呢?最直观直接的方法就是执行一下这个过程看看,例如:

int main() {
    vector<int> v;
    v.push_back(1);
    cout <" " <endl;    // >: 1 1
    v.push_back(2);
    cout <" " <endl;    // >: 2 2
    v.push_back(3);
    cout <" " <endl;    // >: 3 4
    v.push_back(4);
    cout <" " <endl;    // >: 4 4
    v.push_back(5);
    cout <" " <endl;    // >: 5 8
}

这样的容量扩张过程,也带来另一个问题:当容量为 2 时获取到的 iterator ,那么在容量为 8 时,还可以用嘛?答案是不一定行,例如:

int main() {
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    auto iter = v.begin();
    cout <endl;  // >: 1
    v.push_back(4);
    cout <endl;  // >: 1
    v.push_back(5);
    cout <endl;  // >: 310076128[具体运行结果视内存情况而定]
    iter = v.begin();
    cout <endl;  // >: 1
}

由此可知,如果当时的内存环境允许,会直接拼到原有容器后面去,如果不允许,那么就需要把当前容器的内容移动到其他地方去了,这时候原来的 iterator 就不能用了。务必小心!

从这一方面也可以体会到,其实 iterator 就是一个类型为传入 vectorT 类型的指针。

在所有的接口中,觉得最有意思的就是 insert 接口了?,它的实现过程比较好玩。

首先,假设调用函数为:insert(position, n, x) ,而且剩余空间够用,那么它需要分成两种情况:

  1. 插入元素个数 n < 插入点之后的元素个数
  2. 插入元素个数 n > 插入点之后的元素个数

上面两种情况分别对应下面图中的左右两边:

2be73c9cc5116739d8f7875383d22faa.png

内存操作示意图(参考自《STL源码刨析》)

在有限的过程和空间里实现最高效的操作,不愧是STL???。

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

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

相关文章

泰森怎么会输给道格拉斯_泰森24岁就被击败,如果能像他学习巅峰时期至少能延长三年...

一个职业拳击手的运动生涯长短&#xff0c;除了与自身的打法风格有关外&#xff0c;最重要的就是自律&#xff0c;有很多拳手自身天赋极高&#xff0c;但当他成名之后&#xff0c;随着金钱和荣誉的到来往往无法把持自己&#xff0c;最终走向衰退。我们都知道&#xff0c;拳王迈…

数字加密c语言程序_国外程序员整理的 C++ 资源大全

喜欢的话可以收藏转发加关注摘要&#xff1a;C是在C语言的基础上开发的一种集面向对象编程、泛型编程和过程化编程于一体的编程语言。应用较为广泛&#xff0c;是一种静态数据类型检查的&#xff0c;支持多重编程的通用程序设计语言。关于 C 框架、库和资源的一些汇总列表&…

linux操作命令 mongo_Linux安装mongodb总结(仅学习)

原链接&#xff1a;Linux安装mongodb总结 - Lovebugs.cn - 博客园由于自己的博客上线部署时需要用到mongodb来存储图片文件&#xff0c;所以先在本地电脑上安装了mongodb做测试&#xff0c;由于之前没接触过mongodb&#xff0c;所以安装过程中遇到了各种小问题&#xff0c;折腾…

计算机二级必备快捷键知识,计算机二级考试中的一些注意事项️

原标题&#xff1a;计算机二级考试中的一些注意事项️科教武汉 【计算机二级考试中的一些注意事项️】1、要合理安排做题时间可以先通过观察整个题目的题形&#xff0c;判断整个试卷的难点&#xff0c;通过观察题型然后确定自己的应对策。选择题建议用时15-20分钟为好。自己要有…

vba 自定义function返回值_用vba解决excel如何求前面连续为0的个数

领导布置了任务&#xff0c;要求每天统计当月的发展量&#xff0c;并且统计有多少业务员最多连续多少天发展为0的情况&#xff0c;统计的表格是这样的。用几行简单的VBA语言就能解决这个问题首先打开vba编辑窗口&#xff0c;点击开发工具——visual basic(如果没有这个菜单&…

禁用计算机组策略和管理,计算机正在使用时,禁用或关闭组策略刷新 | MOS86

点击此处修复Windows错误并提高系统性能Group Policy(Windows 2000中引入的管理工具)可以确定组织中的用户和计算机的程序&#xff0c;网络资源和操作系统的运行方式。组策略可帮助用户通过在Windows Windows注册表中进行修改来为活动对象添加策略。通常&#xff0c;默认情况下…

怎么捡自己空投_使命召唤手游空投怎么快速找到?使命召唤手游快速找到空投技巧...

使命召唤手游怎么快速寻找空投&#xff1f;空投里面有高级武器跟护甲还有补给品&#xff0c;拿到空投你的装备就会比别人高几个档次。高手拿到空投后吃鸡概率大大提高。下面就为大家详细的介绍一下如何让你快速的找到空投。首先刚开始游戏的时候是不会出现空投的&#xff0c;一…

ubuntu rpm安装_为什么说Ubuntu是一个值得尊敬的Linux发行版

Ubuntu用了好长时间了&#xff0c;最早接触的是RedHat&#xff0c;个人使用体验并不好&#xff0c;当时一直在想&#xff0c;为什么就没有一个更好用的Linux操作系统呢&#xff1f;别万年不变的Windows不行吗&#xff1f;&#xff08;当时作为一个穷学生党&#xff0c;完全不想…

linux怎样自动检查link文件_怎样理解和识别 Linux 中的文件类型 | Linux 中国

众所周知&#xff0c;在 Linux 中一切皆为文件&#xff0c;包括硬盘和显卡等。在 Linux 中导航时&#xff0c;大部分的文件都是普通文件和目录文件。但是也有其他的类型&#xff0c;对应于 5 类不同的作用。因此&#xff0c;理解 Linux 中的文件类型在许多方面都是非常重要的。…

c++ map底层_深入浅出Redisredis底层数据结构(上)

来源:http://t.cn/AigEOwRE相信使用过Redis 的各位同学都很清楚&#xff0c;Redis 是一个基于键值对(key-value)的分布式存储系统&#xff0c;与Memcached类似&#xff0c;却优于Memcached的一个高性能的key-value数据库。在《Redis设计与实现》这样描述&#xff1a;Redis 数据…

绝对布局优势_前瞻布局+尖端科技+雄厚资金 恒大解锁造车的“正确姿势”

新能源汽车行业风起云涌&#xff0c;在造车新势力们交相厮杀之时&#xff0c;恒大闪电般完成“新能源车全产业链布局”&#xff0c;占据了绝对先发优势。先人一步驶入快车道的恒大并未止步于此&#xff0c;5月30日&#xff0c;恒大又宣布拿下英国顶级轮毂电机企业protean&#…

山东省计算机考试无法报名,山东省2017年9月全国计算机等级考试报名事项公告...

根据教育部考试中心《关于做好2017年全国计算机等级考试工作的通知》(教试中心函〔2016〕237号)和《关于做好2017年9月全国计算机等级考试报名工作的通知》(鲁招考[2017]67号)要求&#xff0c;现将2017年9月我省全国计算机等级考试报名有关事项公告如下&#xff1a;一、 考试科…

laravel redis_thinkphp+redis实现秒杀,缓存等功能

秒杀是商城常见功能 phpredis是最常见的秒杀功能1&#xff0c;安装redis&#xff0c;根据自己的php版本安装对应的redis扩展首先查看phpinfo();php环境信息2&#xff0c;下载redishttps://windows.php.net/downloads/pecl/snaps/redis/ https://windows.php.net/downloads/pecl…

freemarker ftl模板_效率提升百分之四十,AS模板也太好用了吧

点击蓝字 轻松关注CV 工程师你好&#xff0c;以下内容会让你感觉到不适&#xff0c;慎入&#xff01;在平时的开发工作中&#xff0c;我们经常将一个地方的代码&#xff0c;复制粘贴到另外一个地方&#xff0c;俗称搬砖&#xff0c;搬砖搬多了&#xff0c;作为一个资深的挨踢民…

python for循环连续输入五个成绩判断等级_Python基础(1)——输入输出/循环/条件判断/基本数据类型...

一、编程语言 1、 编译型语言&#xff1a;先编译&#xff0c;再执行 &#xff08;先编译成二进制&#xff09; 举例&#xff1a;英文书翻译成中文再看 C、C、C# 2、解释型语言&#xff1a;一边执行一边编译 举例&#xff1a;英文书找个翻译&#xff0c;它翻译一行你听一行 php、…

笔记本计算机无法开机怎么办,笔记本开机没反应,教您笔记本电脑开不了机怎么处理...

笔记本电脑使用的时间长了&#xff0c;都是会出现一些小故障&#xff0c;大毛病什么的&#xff0c;比如说像开机后&#xff0c;电源指示灯亮&#xff0c;但是电脑的显示器屏就是没有反应。这个问题让用户慌了神&#xff0c;为此&#xff0c;那笔记本电脑开不了机怎么解决&#…

离群点检测方法_干货 :时间序列异常检测

异常检测(Anomaly detection)是目前时序数据分析最成熟的应用之一,定义是从正常的时间序列中识别不正常的事件或行为的过程。有效的异常检测被广泛用于现实世界的很多领域,例如量化交易,网络安全检测、自动驾驶汽车和大型工业设备的日常维护。以在轨航天器为例,由于航天器昂…

centos6安装mysql并远程连接_MySQL5.7数据库安装与远程连接

系统&#xff1a;Redhat Linux 7.7MySQL版本&#xff1a;5.71. MySQL下载o 登录网站&#xff1a;https://dev.mysql.com/downloads/mysql/o 选择合适版本与系统Select version&#xff1a;5.7.31Select operating system&#xff1a;Red Hat Enterprise LinuxSelect OS Version…

python小代码_Python爬虫入门有意思的小长代码

一段有意思的代码&#xff0c;有兴趣的可以研究研究。 需求 用户收到短信如&#xff1a;购买了电影票或者火车票机票之类的事件。然后app读取短信&#xff0c;解析短信&#xff0c;获取时间地点&#xff0c;然后后台自动建立一个备忘录&#xff0c;在事件开始前1小时提醒用户。…

小学生 计算机编程 教程,面向小学生的C ++有趣编程(第一卷)配置教学资源课件完整版...

难度适中&#xff0c;易学易教的“面向小学生的C 趣味编程”教科书是小学信息学教学的重要组成部分.选择80多个接近小学生学习生活的例子&#xff0c;结合小学生的认知规律&#xff0c;激发孩子的兴趣&#xff0c;关注程序&#xff0c;适当削弱语法.使用流程图来阐明思想&#…