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

【C++】STL--Vector使用极其模拟实现

目录

1. Vector的介绍

2. Vector的使用

2.1. vector的定义

2.2. vector迭代器的使用

2.3. vector的空间增长问题

3. vector中的增删改查

3.1. push_back(尾插)

3.2. pop_back(尾删)

3.3. insert

3.4 erase

3.5 operator [ ] 

4. 测试


1. Vector的介绍

官方文档介绍

  • vector表示可变大小数组的序列容器
  • 与数组一样,vector采用的是使用连续空间来储存元素,那也就意味着,采用下标对vector进行访问和数组一样高效。但是相较于数组大小的固定,vector的大小是动态可变的,并且这个变化是由容器自动处理的
  • 本质上讲,vector是动态分配数组大小实现的,当有新元素插入时,当需要扩容空间时,数组的大小将会重新分配。具体的做法是,重新开辟一块更大的内存,然后将原数组的元素拷贝过去(很明显是一个 代价很高的任务)
  • vector的空间分配策略:每一次分配空间都是按照对数级增长(一般是1.2或者是1.5),形象地来讲就是一种囤地策略,每次重新分配都会多囤一点,这样的话插入时,有地方就话直接插入,如此每一次重新分配内存的高额代价就会被多囤地的免费插入摊平,总的来看,末尾插入的时间代价还是常数
  • 总的来说,vector访问元素、在末尾添加删除元素比较高效,但是其他操作相对效率低

2. Vector的使用

vector在日常的使用非常广泛,我们应该熟悉它的常用接口。接下来我们从基础的接口开始,学会它的使用及模拟实现。

vector模拟实现的基本结构:

template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;//无参构造vector():_start(nullptr)	//数组首地址, _finish(nullptr)	//当前末尾元素的下一个地址, _endofstoage(nullptr)	//当前分配空间的下一地址{}//资源管理~vector(){if (_start){delete[] _start;_start = _finish = _endofstoage = nullptr;}}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstoage - _start;}
private:iterator _start;iterator _finish;iterator _endofstoage;
};

2.1. vector的定义

构造函数声明constructor接口说明
vector() (重点)无参构造
vector (const vector& x); (重点)拷贝构造
vector ( size_type n, const value_type& val = value_type() )构造并初始化 n 个 val
vector (InputIterator first, InputIterator last);使用迭代器进行初始化构造
//无参构造
vector():_start(nullptr), _finish(nullptr), _endofstoage(nullptr)
{}//拷贝构造
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstoage, v._endofstoage);
}//vector(const vector& v)
vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _endofstoage(nullptr)
{vector tmp(v.begin(), v.end());swap(tmp);
}//初始化n个val 
vector(size_t n, const T& val = T()):_start(nullptr), _finish(nullptr), _endofstoage(nullptr)
{reserve(n);for (size_t i = 0; i < n; ++i){push_back(val);}
}//使用迭代化区间初始化
template <class InputIterator>
vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _endofstoage(nullptr)
{while (first != last){push_back(*first);++first;}
}

2.2. vector迭代器的使用

iterator的使用接口说明
begin+end (重点) 获取第一个数据位置的 iterator/const_iterator , 获取最后一个数据的下一个位置的 iterator/const_iterator
rbegin+rend(反向迭代器)获取最后一个数据位置的 reverse_iterator ,获取第一个数据前一个位置的reverse_iterator
iterator begin()
{return _start;
}
iterator end()
{return _finish;
}const iterator begin() const
{return _start;
}
const iterator end() const
{return _finish;
}

2.3. vector的空间增长问题

容量空间接口说明
size获取数据个数
capacity获取容量大小
empt判断是否为空
resize(重点)改变 vector 的 size
reserve(重点) 改变 vector 的 capacity
void resize(size_t n, T val = T())
{if (n > capacity()){reserve(n);}if (n > size()){while (_finish <= _start + n){*_finish = val;++_finish;}}else{_finish = _start + n;}
}void reserve(size_t n)
{size_t sz = size();if (n > capacity()){T* tmp = new T[n];if (_start){for (int i = 0; i < sz; ++i){tmp[i] = _start[i];}delete[] _start;}_start = tmp;}_finish = _start + sz;_endofstoage = _start + n;}

注意:

vector在扩容的时候有一个小细节,那就是vector的扩容在vs和gcc下是有区别的,vs下是1.5倍扩容,然而在在gcc下是2倍扩容。所以不能固话地认为vector的扩容就是2倍。具体增长的多少要根据需求定义。Vs是PJ盘本的STL,g++是SGI版本的STL。

我们可以在VS和Clion两个环境下试一下:

//vs下
int main()
{vector<int> v;size_t sz = v.capacity();for (int i = 0; i < 100; ++i){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}return 0;
}

VS中的结果:

Clion中gcc的结果:


3. vector中的增删改查

vector 增删查改

接口说明

push_back (重点)

尾插

pop_back (重点)

尾删

find

查找。(注意这个是算法模块实现,不是 vector 的成员接口)

insert

在 pos 之前插入 val

erase

删除 pos 位置的数据

swap

交换两个 vector 的数据空间

operator[] (重点)

像数组一样访问

3.1. push_back(尾插)

//尾插
void push_back(const T& x)
{if (_finish == _endofstoage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;++_finish;
}

3.2. pop_back(尾删)

这个地方熟悉一点的同学都知道,尾删的逻辑是覆盖而并不是删除:

//尾删
void pop_back(const T& x)
{if (_finish > _start){--_finish;}
}

3.3. insert

  • 首先要判断pos是否合法
  • 然后判断是否需要扩容,如果需要这里采用了一个相对量来保证pos的正确性
  • 然后从后往前移动元素,前面的覆盖后面的
  • 最后插入值,然后更新_finish,返回pos
iterator insert(iterator pos, const T& x)
{assert(pos <= _finnsh && pos >= _start);if (_finish == _endofstoage){//这里使用pos到首地址的偏移量//因为扩容之后原先pos的失效了size_t n = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + n;}iterator end = _finish - 1;while(end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}

PS:注意这里我们将pos声明为iterator,可能有人会问为什么不使用下标更加方便呢?当时在这里使用下标确实会更加方便,但是这个容器不是vector了,而是list或者map,就并不适配。


3.4 erase

  • 判断pos是否合法
  • 然后循环congpos开始,后一个覆盖前一个
  • 更新_finish
  • 返回pos
iterator erase(iterator pos)
{assert(pos >= _start && pos <= _finish);iterator end = pos + 1;while (end < _finish){*(end - 1) = *end;++end;}--_finish;return pos;
}

3.5 operator [ ] 

这个函数比较简单,只要返回pos所在位置的元素即可:

T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos) const
{assert(pos < size());return _start[pos];
}

4. 测试

int main() {// 1. 默认构造 + push_backv::vector<int> v1;for (int i = 1; i <= 5; i++) {v1.push_back(i * 10);}cout << "v1 after push_back: ";for (size_t i = 0; i < v1.size(); i++) {cout << v1[i] << " ";}cout << "\n";// 2. 测试 insertauto it = v1.begin() + 2;v1.insert(it, 99);cout << "v1 after insert 99 at pos 2: ";for (size_t i = 0; i < v1.size(); i++) {cout << v1[i] << " ";}cout << "\n";// 3. 测试 erasev1.erase(v1.begin() + 3);cout << "v1 after erase at pos 3: ";for (size_t i = 0; i < v1.size(); i++) {cout << v1[i] << " ";}cout << "\n";// 4. 测试 pop_backv1.pop_back();cout << "v1 after pop_back: ";for (size_t i = 0; i < v1.size(); i++) {cout << v1[i] << " ";}cout << "\n";// 5. 测试拷贝构造v::vector<int> v2 = v1;cout << "v2 (copy of v1): ";for (size_t i = 0; i < v2.size(); i++) {cout << v2[i] << " ";}cout << "\n";// 6. 测试初始化构造 (5个元素,值全为 7)vector<int> v3(5, 7);cout << "v3 (5 elements = 7): ";for (size_t i = 0; i < v3.size(); i++) {cout << v3[i] << " ";}cout << "\n";// 7. 测试迭代器区间构造v::vector<int> v4(v1.begin(), v1.end());cout << "v4 (constructed from v1): ";for (size_t i = 0; i < v4.size(); i++) {cout << v4[i] << " ";}cout << "\n";return 0;
}


(本篇完)


文章转载自:

http://aBNfuvFF.Lbfgq.cn
http://PhjABKxF.Lbfgq.cn
http://VVLonGpa.Lbfgq.cn
http://VfM1lGsQ.Lbfgq.cn
http://B6WwFXXI.Lbfgq.cn
http://M487yjgO.Lbfgq.cn
http://9d1tzso7.Lbfgq.cn
http://s3zNwpNc.Lbfgq.cn
http://zlaKCqXd.Lbfgq.cn
http://SGxHyECT.Lbfgq.cn
http://5vnE3bQC.Lbfgq.cn
http://jz10DBZu.Lbfgq.cn
http://AhyxZea0.Lbfgq.cn
http://g6ItmKN4.Lbfgq.cn
http://aLuSoE7E.Lbfgq.cn
http://oGGagLI0.Lbfgq.cn
http://GvC5hJJc.Lbfgq.cn
http://PNIahOTO.Lbfgq.cn
http://qyKo3sue.Lbfgq.cn
http://ZBRwg2p2.Lbfgq.cn
http://9j5Vv0hh.Lbfgq.cn
http://MkTlJROy.Lbfgq.cn
http://y2cSkWnj.Lbfgq.cn
http://aVbkGNYr.Lbfgq.cn
http://T4E4ZF60.Lbfgq.cn
http://Nnodotcl.Lbfgq.cn
http://B7ifsKKU.Lbfgq.cn
http://szOAxOrk.Lbfgq.cn
http://xFYAx3oC.Lbfgq.cn
http://A9uU65Ox.Lbfgq.cn
http://www.dtcms.com/a/378339.html

相关文章:

  • QT子线程与GUI线程安全交互
  • 论 Intel CPU 进化史:德承工控机全面进化 搭载新一代 Intel® Core™ Ultra 7/5/3 处理器
  • 论文阅读/博弈论/拍卖:《Truthful Auction for Cooperative Communications》
  • 【论文阅读】Towards Privacy-Enhanced and Robust Clustered Federated Learning
  • [论文阅读] 告别“数量为王”:双轨道会议模型+LS,破解AI时代学术交流困局
  • 【UE】2D SphereNormalsMap - 实时计算2D “球形法线” 贴图
  • 保护模式下的特权级_考研倒计时 100 days
  • 中科米堆CASAIM高精度蓝光3D扫描激光抄数服务逆向三维建模
  • 【Canvas与几何图案】六钩内嵌大卫之星黑白图案
  • 智能体工作流画布:提升企业业务流程自动化效率
  • 如何从 iPhone 打印联系人信息
  • FOC系列(六)----学习DRV8313/MS8313芯片,绘制驱动板
  • Android开发值Android官方模拟器启动失败问题跟踪排查
  • hardhat 项目目录介绍
  • IROS 2025 多智能体深度强化学习算法实现Crazyflie无人机在复杂环境中协同追逐
  • 光平面标定 (Laser Plane Calibration) 的原理和流程
  • sqbks二面(准备)
  • Linux云计算系统安全:PAM
  • DenseNet详解与实现
  • 计算机毕业设计 基于Hadoop豆瓣电影数据可视化分析设计与实现 Python 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试
  • 25.9.11 QTday1作业
  • unity 陶艺制作模拟
  • Unity 三维数学方法
  • 【氮化镓】GaN基半导体器件电离辐射损伤基可靠性综述
  • 音视频demo
  • 相机Camera日志分析之三十六:相机Camera常见日志注释
  • 250911算法练习:递归
  • 双目相机原理
  • AI教育白皮书解读 | 医学教育数智化转型新机遇,“人工智能+”行动实践正当时
  • vue3自定义无缝轮播组件