当前位置: 首页 > 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.dtcms.com/a/269485.html

相关文章:

  • 虚幻引擎UE5专用服务器游戏开发-20 添加基础能力类与连招能力
  • Takebishi旗下智能硬件网关产品devicegateway详细介绍
  • ThreadPoolTaskExecutor 的使用案例
  • 【PTA数据结构 | C语言版】求单链表list中的元素个数,即表长
  • NumPy-随机数生成详解
  • AI编程的未来是智能体原生开发?
  • JavaSE-继承
  • UI前端与数字孪生结合实践案例:智慧零售的库存管理优化系统
  • 算法学习笔记:10.Prim 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
  • 【Mac】实现Docker下载安装【正在逐步完善】
  • 【论文阅读】CogVideoX: Text-to-Video Diffusion Models with An Expert Transformer
  • 【计算机基础理论知识】C++篇(一)
  • 暑假读书笔记第四天
  • 【Python-GEE】如何利用Landsat时间序列影像通过调和回归方法提取农作物特征并进行分类
  • python transformers库笔记(BertForTokenClassification类)
  • 【牛客刷题】小红的与运算
  • node.js中yarn、npm、cnpm详解
  • 精益管理与数字化转型的融合:中小制造企业降本增效的双重引擎
  • 算法训练营DAY29 第八章 贪心算法 part02
  • 实战Linux进程状态观察:R、S、D、T、Z状态详解与实验模拟
  • 联通线路物理服务器选择的关键要点
  • No Hack No CTF 2025Web部分个人WP
  • Django双下划线查询
  • 微信小程序控制空调之接收MQTT消息
  • 如何利用AI大模型对已有创意进行评估,打造杀手级的广告创意
  • deepseek实战教程-第九篇开源模型智能体开发框架solon-ai
  • Python爬取知乎评论:多线程与异步爬虫的性能优化
  • React18+TypeScript状态管理最佳实践
  • Jenkins 使用宿主机的Docker
  • 深入解析 structuredClone API:现代JS深拷贝的终极方案