当前位置: 首页 > news >正文

C++(STL源码刨析/vector)

目录

vector的实现:

1. 先来看看vector前置的类型定义:

2. 在来看看空间配置器:

3. 在来看看核心3个管理内存块的字段:

4. 构造函数/析构函数;

5. 其他成员接口:

下面来看看上述接口的具体实现;


vector的实现:

1. 先来看看vector前置的类型定义:

typedef T value_type;                                :对象类型
typedef value_type* pointer;                      :对象指针   
typedef value_type* iterator;                      :对象迭代器
typedef value_type& reference;                 :对象引用
typedef size_t size_type;                            :对象的个数
typedef ptrdiff_t difference_type;                : 用来计算对象的个数

可以看出来指针和迭代器是一样的,因为vector是线性的,直接用裸指针就能模拟迭代器的行为,这里不需要过多考虑。

2. 在来看看空间配置器:

typedef simple_alloc<value_type,Alloc> data_allocator;

前面章节讲了 simple_alloc 是对一级/二级空间配置器内部的申请内存/释放内存接口的封装,具体用哪个配置器,取决于 __USE_MALLOC 这个宏来决定。

3. 在来看看核心3个管理内存块的字段:

Iterator start;                        :内存块的起始地址

Iterator fnish;                        :内存块的实际总共用了的结束地址

Iterator end_of_storage;        :整个内存块的结束地址

可以看出来在源码这里采用迭代器定义了3个核心字段,也就是指针,指针也能和指针进行运算,其实只需要知道内存块的起始地址 + 实际用了的大小 + 总共的大小也能代替后2个字段用指针的情况。

4. 构造函数/析构函数;

vector():start(0),finish(0),end_of_stroage(0) { }                       :无参构造

vector(size_type n,const T& value) { fill_initialize(n,value); }  :指定大小,指定对象拷贝构造

vector(int n,const & value) { fill_initialize(n,value); }               :指定大小,指定对象拷贝构造

vector(long n,const T& value) { fill_initialize(n,value); }          :指定大小,指定对象拷贝构造

explicit vector(size_type n) { fill_initialize(n,T()); }                 

                                                                        :指定大小,指定对象默认构造并防类型转换

~vector();                                                        :析构 vector 对象

5. 其他成员接口:

void insert_aux(iterator position,const T& x);                :插入元素

void deallocate();                                                          : 释放内存块

void fill_initialize(size_type n,const T& value);              :指定大小,指定对象拷贝构造

iterator allocate_and_fill();                                             :指定大小,指定对象拷贝构造

iterator begin();                                                               :起始地址

iterator end();                                                                  :结束地址

size_type size() const;                                                    : 实际用了的大小

bool empty() const;                                                         :是否为空

reference operator[ ](size_type n);                                 :重载 [ ]
reference front();                                                             :取头部元素

reference back();                                                            :取尾部元素

void push_back(const T&);                                             :尾部插入元素

void pop_back();                                                             :尾部删除元素

iterator erase(iterator position);                                      :删除指定元素

void resize(size_type new_size,const T&);                    :扩容

void resize(size_type new_size);                                   :扩容

void clear();                                                                     :只是让指针归0,并不会释放内存

下面来看看上述接口的具体实现;

template <class T,class Alloc = aloc>
class vector
{public:typedef T value_type;                              typedef value_type* pointer;                    typedef value_type* iterator;                 typedef value_type& reference;               typedef size_t size_type;                       typedef ptrdiff_t difference_type;               protected:typedef simple_alloc<value_type,Alloc) data_allocator;iterator start;iterator finish;iterator end_of_storage;void deallocate(){// start 不为空则调用空间配置器的释放函数,注意不是析构,仅仅是释放if(start) data_allocator::deallocate(start,end_of_storage -  start);}    void fill_initialize(size_type n,const T& value){start = allocate_end_fill(n,value);        // 分配内存并构造finish = start + n;                        // 调整实际的元素个数end_of_storage = finish;                   // 调整整个内存块的结束地址}iterator allocate_and_fill(size_type n,const T& x){// 调用空间配置器的申请对象接口iterator result = data_allocator::allocate(n);// 并调用全局函数进行构造对象uninitialized_fill_n(result,n,x);return result;// 上面2个函数前章讲过}public:iterator begin(){ return start; }        // 返回内存块的起始地址iterator end(){ return end; }            // 返回内存块实际使用的元素个数的地址size_type size() const { return size_type(end() - begin()); }    // 返回实际的元素个数size_type capacity() const { return size_type(end_of_storage - begin()); }// 返回容量bool empty() const { return begin() == end(); }    // 判断是否为空reference operator[](size_type n){ return *(begin() + n); }   // 重载 []reference front(){ return *begin(); }                         // 返回头部元素reference back(){ return *(end() - 1); }                      // 返回尾部元素void push_back(const T& x)                                    // 插入对象{// 如果还有剩余空间if(finish != end_of_storage)                    {// 并构造对象construct(finish,x);++finish;}// 否则尾插并扩容else insert_aux(end(),x);}    void pop_back()                        // 删除尾部元素{--finish;destroy(fnish);                    // 析构元素}    iterator erase(iterator position)      // 删一个{// 区分边界情况,不能等于实际有效元素的下一个迭代器if(position + 1 != end()) copy(position + 1,finish,position);--finish;destroy(finish);    // 析构元素return position;    // 返回下一个迭代器}iterator erase(iterator first,iterator last)    // 删一个区间{iterator i = copy(last,finish,first);   // 删除区间并返回 finish - 区间大小的迭代器destroy(i,finish);                      // 析构这个区间后面到finish的元素finish = finish - (last - first);       // 更新元素return frsit;                           // 返回区间的起始地址}void resize(size_type new_size,const T& x)    // 扩容/缩容{    // 如果指定大小小于实际的元素个数,则删除,注意这里只是改变了指针指向,并不会释放if(new_size < size()) erase(begin() + new_size,end());else insert(end(),new_size - size(),x);    // 否则进行插入扩容,并构造}void resize(size_type new_size) { resize(new_size,T()); }    // 只扩容,不构造void clear() { erase(begin(),end()); }        // 清楚内存块,不释放protected:void insert_aux(iterator position,const T& x)    // 扩容插入元素{// 前面push_back已经检查了,但这个函数可能被多个接口调用,双重保险if(finish != end_of_storage){construct(fnish,x);        // 还有元素就直接构造对象    ++finish;                  // 调整尾指针T x_copy = x;              // 防止插入自己内部的元素导致元素被覆盖导致数据不一样         copy_backward(position,finish - 2,finish - 1);    // 反向挪数据*position = x_copy}    else                           // 容量不够{const size_type old_size = size();                        // 记录旧空间const size_type len = old_size != 0? 2 * old_size : 1;    // 一开始为空就给一// 个,否则给2倍的旧空间iterator new_start = data_allocator::allocate(len);       // 申请新的空间iterator new_finish = new_start;                          // 更新尾指针try    // 拷贝旧数据可能失败捕获异常{new_finish = uninitialized_copy(start,position,new_start);construct(new_finish,x);++new_finish;new_finish = uninitialized_copy(position,finish,new_finish);}catch(...)    // 拷贝旧数据失败旧把新申请的空间进行析构和释放{destroy(new_start,new_finish);data_allocator::deallocate(new_start,len);throw;}// 析构旧空间和释放destroy(begin(),end());deallocate();// 更新3个指针start = new_start;finish = new_finish;end_of_storage = new_start + len;}}void insert(iterator position,size_type n,const T& x){if(n != 0)                // 至少插入一个元素{if(size_type(end_of_storage - finish) >= n)          // 剩余空间满足需要的大小{T x_copy = x;                                    // 和前面的insert_aux一样const size_type elems_after = finish - position; // 记录pos到尾的长度iterator old_finish = finish;                    // 记录尾迭代器if(elems_after > n)                      // 如果pos后面的实际有效长度大于n{// 先把 finish - n 到 finish 这个区间元素挪到 finish后面uninitialized_copy(finish - n,finish,finish);// 调整 finishfinish += n;// 在把 pos 指向的元素挪到 finish - n 的元素挪到旧的finish后面copy_backward(position,old_finish - n,old_finish);// 在从 pos 位置填充fill(position,position + n,x_copy);[ 1 , 2 , 3 , 4 , 5 , 6 , _ , _ , _ , _ ] // 假设在4插入2个 -1// 先把 finish - n -> 指向 5 ~ finish(6) 挪到finish后面,6的后面[ 1 , 2 , 3 , 4 , 5 , 6 , 5 , 6 , _ , _ ]// 调整finish到最后一个6的下一个位置// 在把 pos(4) 到 旧的(finish - n)(第一个5,这是开区间不算5),此时就值挪动一个4// 挪到旧的finish位置,第一个6 [ 1 , 2 , 3 , 4 , 5 , 4 , 5 , 6 , _ , _ ]// 在把 pos 到 pos + n ,也就是 2 个位置进行拷贝   //    [ 1 , 2 , 3 , -1 , -1 , 4 , 5 , 6 , _ , _ ]}    else                                    // 如果pos后面的实际有效长度大于n{//  [ 1 , 2 , 3 , 4 , 5 , 6 , _ , _ , _ , _ ]// 假设在5位置插入3个-1// 先从 finish 开始 拷贝 n - elems_after 个元素:3 - 2 个 -1  // [ 1 , 2 , 3 , 4 , 5 , 6 , -1 , _ , _ , _ ]// 在调整 finish 到最后一个-1的后面// 在从 pos 位置到 旧的finish 也就是 5,6拷贝到新的finish后面,-1的后面// [ 1 , 2 , 3 , 4 , 5 , 6 , -1 , 5 , 6 , _ ]// 最后从 pos 位置到旧的finish这个区间逐个 拷贝 -1//  [ 1 , 2 , 3 , 4 , -1 , -1 , -1 , 5 , 6 , _ ]uninitialized_fill_n(finish, n - elems_after,x_copy);finish += n - elems_after;ininitialized_copy(position,old_finish,finish);fill(position,old_finish,x_copy);}}else{// 计算旧的实际的元素个数const size_type old_size = size();// 计算极端情况 n 比2个旧元素个数还大的情况const size_type len = old_size + max(old_size,n);// 重新 申请空间iterator new_start = data_allocator::allocate(len);iterator new_finish = new_start;__STL_TRY        // 把原数据拷贝到新的空间里{new_finish = uninitialized_copy(start,position,new_start);new_finish = uninitialized_fill_n(new_finish,n,x);new_finish = uninitialized_copy(position,finish,new_finish);}#ifdef __STL_USE_EXCEPTIONS    // 异常则析构释放新申请的空间catch(...){destroy(new_start,new_finish);data.allocator::deallocate(new_start,len);throw;}#endif    // 析构释放旧的空间destroy(start,finish);deallocate();// 最后在更新成员3个核心字段start = new_start;finish = new_finish;end_of_storage = new_start + len; }}}
}

http://www.lryc.cn/news/583003.html

相关文章:

  • 从历史航拍图像中去除阴影
  • 11款常用C++在线编译与运行平台推荐与对比
  • 力扣-75.颜色分类
  • Web后端开发-Mybatis
  • qt-C++笔记之setCentralWidget的使用
  • 软件系统测试的基本流程
  • 数据结构*搜索树
  • 从零开始手写嵌入式实时操作系统
  • 牛市来临之际,如何用期权抢占反弹先机?
  • 初识mysql(一)
  • [特殊字符] AlphaGo:“神之一手”背后的智能革命与人机博弈新纪元
  • 【深度学习新浪潮】什么是蛋白质反向折叠模型?
  • 深度学习超参数优化(HPO)终极指南:从入门到前沿
  • FairyGUI 实现 Boss 双层血条动画
  • qt-C++语法笔记之Stretch与Spacer的关系分析
  • 分库分表之实战-sharding-JDBC水平分库+水平分表配置实战
  • LeetCode题解---<三数之和>
  • 自动化一次通过率
  • 深度学习环境配置:PyTorch、CUDA和Python版本选择
  • 深度剖析:向70岁老系统植入通信芯片——MCP注入构建未来级分布式通信
  • 模型训练篇 | 如何用YOLOv13训练自己的数据集(以明火烟雾检测举例)
  • HTML+JS+CSS制作一个数独游戏
  • 原生屏幕旋转算法(AccelSensor)
  • 力扣-31.下一个排列
  • Python打卡:Day47
  • 【排序】插入排序
  • 单调栈通关指南:从力扣 84 到力扣 42
  • eslint扁平化配置
  • IoTDB:专为物联网场景设计的高性能时序数据库
  • 深圳凭物联网软件开发构建智慧‘城市大脑‘