news 2026/10/8 18:30:14

【C++】STL源码仿写(二):vector

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C++】STL源码仿写(二):vector

一、vector概览

上一篇博客中我们介绍了string的简化实现,今天我们来介绍另一个STL当中比较重要的容器——vector。

private 字段:数据结构
在我们的实现中,vector作为通用容器需要兼容不同的数据/对象类型,所以我们需要用模板这个特性,为此,我们涉及data_字段为T*类型,其中T是模板名,内存中为数组所分配的内存长度是capacity_,实际长度是size_。

public 方法:对外接口
对外接口的实现上,重点考虑push_back()和pop_back()。的实现需要考虑左右值的兼容,为此,我们用到了完美转发、万能引用,还需要考虑扩容的逻辑。pop_back方法的实现比较简单,就是要记得调用容器中对象的析构函数。

Rule of five
我们的vector实现有对堆上的资源进行管理,所以需要rule of five,但是只实现析构函数;由于只使用index找值,且只调用push_back和pop_back,所以拷贝构造、移动构造、拷贝赋值、移动赋值可以都禁止掉。

二、vector实现

我们使用默认构造和默认成员初始化的办法,这样避免过度复杂化实现:

classvector{public:vector()=default;private:T*data_=nullptr;size_t size_=0;size_t capacity_=0;};

现在考虑实现析构函数,析构函数的作用有两个:1.逐个调用容器中对象的析构函数,清空容器;2.释放已分配的内存。这里注意clear()方法是STL vector的公有方法,所以单独拿出来。释放内存的实现使用operator detele,因为之前clear方法已经调用了析构函数了。

~vector(){clear();//清空容器deallocate();//释放内存}voidclear(){for(size_t i=0;i<size_;i++){data_[i].~T();}size_=0;}private:voiddeallocate(){::operatordelete(data_);data_=nullptr;capacity_=0;}

这里顺便实现一下allocate():注意我们的vector实现中构造与内存分配总是采取分离的策略,所以也使用operator new。operator new返回的类型是void*,所以需要类型转换

T&allocate(){returnstatic_cast<T*>(::operatornew(capacity_*sizeof(T)));}

现在重头戏来了,让我们来实现push_back()方法。

push_back要考虑到左右值两种的情况。我们这里使用emplace_back作为统一的入口,用变参模板+完美转发+万能引用保留参数数目和值类别;emplace_back:在扩容后使用placement new原地构造对象。

voidpush_back(constT&value){emplace_back(value);}voidpush_back(T&&value){emplace_back(std::move(value));}template<typename...Args>//变参模板T&emplace_back(Args&&...args){//万能引用if(size_==capacity_){reallocate(capacity_==0?1:capacity_*2);//扩容}new(data_+size_)T(std::forward<Args>(args)...);//placement newreturndata_[size_++];//提前位移,为下一个emplace对象腾位置}

现在来实现reallocate这个函数:

  • 分配新内存
  • 复制数据到新内存(placement new)
  • 如果抛异常需要清空新内存的数据
  • 清空原内存数据,释放内存
  • 设置新的cap、sz,data指向新内存
voidreallocate(size_t new_cap){T*new_data=allocate(new_cap);//分配新内存size_t new_size=0;try{for(;new_size<size_;new_size++){//placement new + 移动优化new(new_data+new_size)T(std::move_if_noexcept(data_[new_size]));}}catch(...){//中间抛出异常时清空、释放for(size_t i=0;i<new_size;i++){new_data[i].~T();}::operatordelete(new_data(i));throw;//rethrow,向上传播异常}clear();deallocate();size_=new_size;capacity_=new_cap;data_=new_data;}

接下来实现pop_back(),popback比较简单,就是将最后一个元素从data中删去,调析构、size–即可。我们顺便实现back方法,pop_back一般会配合back使用。

T&back(){returndata_[size_-1];}constT&back()const{returndata_[size_-1];}//const对象的情况要特别处理voidpop_back(){if(size_>0){data_(--size_).~T();}}

现在只剩下一些比较简单的方法还没实现:

T&operator[](size_t index)noexcept{returndata_[index];}constT&operator[](size_t index)constnoexcept{returndata_[index];}size_tsize(){returnsize_;}size_tcapacity(){returncapacity_;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 18:29:07

小白也能看懂!CyberStrikeAI让你的网络安全学习更简单(收藏版)

小白也能看懂&#xff01;CyberStrikeAI让你的网络安全学习更简单&#xff08;收藏版&#xff09; CyberStrikeAI是一个AI原生的网络安全智能执行中枢&#xff0c;它将自然语言意图转化为受控、可审计的安全行动&#xff0c;并连接规划、执行、人工监督、证据与复盘。该平台基…

作者头像 李华
网站建设 2026/10/8 18:28:56

Redis 7.4.1升级到7.4.6完整实战:备份、切换、验证与回滚

上个月我刚好把一台CentOS 7.9上的Redis从7.4.1升级到了7.4.6&#xff0c;说实话一开始心里也没底。Redis做中间件在线上扛着不少流量&#xff0c;最怕版本一升、内存数据出幺蛾子。朋友问我“升级Redis不就是下载新包覆盖一下吗”&#xff0c;必须纠正&#xff1a;核心不是覆盖…

作者头像 李华
网站建设 2026/10/8 18:28:47

智诺方AI|外文翻译段落降重降AIGC技巧,外文文献翻译修改避坑

智诺方AI&#xff5c;外文翻译段落降重降AIGC技巧&#xff0c;外文文献翻译修改避坑 官网 https://www.znfai.cn&#xff0c;微信服务号搜一搜 智诺方 ai 很多本科、研究生毕业论文&#xff0c;要求附带外文文献翻译章节。外文翻译是将国外期刊、著作的原文翻译成中文&#xff…

作者头像 李华
网站建设 2026/10/8 18:26:46

智诺方 AI|论文修改两步走:分清降重和降 AIGC,别再踩修改误区!智诺方AI|论文修改两步走:分清降重和降AIGC,别再踩修改误区!

智诺方AI&#xff5c;论文修改两步走&#xff1a;分清降重和降AIGC&#xff0c;别再踩修改误区&#xff01;官网 https://www.znfai.cn&#xff0c;微信服务号搜一搜 智诺方 ai 很多本科、硕博生在论文定稿阶段都会陷入同一个误区&#xff1a;认为降重就等于降低AIGC检测风险&a…

作者头像 李华
网站建设 2026/10/8 18:23:30

LeetCode 1.两数之和

刷题日记1今天刷了 LeetCode 的第一道经典题&#xff1a;两数之和。虽然是入门题&#xff0c;但非常适合用来理解「暴力枚举」和「哈希表优化」的思维差距&#xff0c;也是算法的基础开胃题&#xff0c;记录一下自己的解题思路。题目大意给一个数组和一个目标值&#xff0c;找出…

作者头像 李华