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

C++ :vector的模拟

目录

一、vector的迭代器

二、vector的构造函数

默认构造函数

参数构造函数

迭代器范围构造函数

拷贝构造函数

swap: 交换vector 

重载赋值符

析构函数

reserve  :   扩容vector

resize  :  调整大小

push_back : 添加元素

empty : 判空

 pop_back : 后删

 获取大小与容量 : size() , capacity() 

重载operator[] : 元素访问

insert : 插入元素

erase:删除一个元素

类的数据成员:

自定义 vector 类的实现

总结


在实际应用中,建议使用标准库stl提供的容器,因为它们经过了优化和充分测试。然而,深入了解vector内部实现无疑会使你加深对 vector 的理解,还能培养解决复杂编程问题的能力。


一、vector的迭代器

template <class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;//typedef const T* iterator const;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin()const{return _start;}const_iterator end()const{return _finish;}
  • begin() : 用于返回空间的首地址
  • end()    : 用于返回空间的尾部的下一个地址

  • begin() const : 返回指向常量版本的vector开始地址
  • begin() const : 返回指向常量版本的vector尾部地址

二、vector的构造函数

默认构造函数

	vector(){}
  •  默认构造函数不执行任何操作,初始化时所有指针均为 nullptr

参数构造函数

vector(size_t n,const T& val=T())
{resize(n, val);
}
vector(int n, const T& val = T())
{resize(n, val);
}
  • 把size_t 和 int 类型的参数分开是为了区分  
////vector<int> v(10,1);10 是int 型更倾向于 InputIterator first 而不是size_t n;
template <class InputIterator>
vector(InputIterator first,InputIterator last)
  • 该构造函数创建一个包含 n 个元素的 vector,每个元素都被初始化为val

迭代器范围构造函数

//[ first , last )
template <class InputIterator>
vector(InputIterator first,InputIterator last)
{while (first != last){push_back(*first);first++;}
}
  • 构造函数接受迭代器first 和 last,依次将区间内的元素添加到 vector 中。
  • 范围是  [first , last) 

拷贝构造函数

vector(const vector<T>& v)//拷贝构造
{//way one:_start = new T[v.capacity()];for (size_t i = 0; i < se; i++){_start[i] = v._start[i];}_finish = _start + v.size();_end = _start + v.capacity();//way two:reserve(v.capacity());for (auto e : v){push_back(e);}} 
  •  有两种方式 

swap: 交换vector 

void swap(vector<T>& v)
{std::swap(_start, v.start);std::swap(_finish, v._finish);std::swap(_end, v._end);
}
  •  交换当前容器与另一个容器的内部指针

重载赋值符

vector<T>& operator=(vector<T> v)//v相当于tmp
{/*vector v1 = vector(v);return v1;*/swap(v);return *this;
}
  •  v是通过深拷贝来的相当于一个临时值tmp

析构函数

~vector()
{if (_start){delete[] _start;_start = _finish = _end = nullptr;}
}
  •  释放分配的内存,并将所有指针设置为 nullptr,防止野指针

reserve  :   扩容vector

void reserve(size_t n)
{if (n > capacity()){ size_t se = size();//要先保存size(),确保_finish 的位置T* tmp = new T[n];if (_start) {/*memcpy(tmp, _start, sizeof(T) * se);delete[] _start;*/   for (size_t i = 0; i < se; i++){tmp[i] = _start[i];}delete[]_start;}_start = tmp;//错误:_finish=_start+size();//size()是_finish-_start 而此时_start 已经改变_finish = _start + se;//所以要先保存原先的size()_end = _start + n;//_start 和_finish 和_end 都是 指针指向地址}
}
  •  reserve扩容是只扩不缩的
  • 由于我们要进行扩容的时候默认采取异地扩容,所以当进行异地扩容的时候,_start指向的空间必然会被释放,并且_start指向新空间,那么在调用size的时候获取的当前元素的有效个数大小就必然会不正确,所以在进行正式扩容之前,我们提前使用sz变量保存一下当前元素的有效个数
  • 使用tmp指向使用new[]申请n个T类型的大小的空间,接下来就是拷贝_start指向空间上的数据到tmp指向的空间上 使用 memcpy函数 进行按字节数进行拷贝数据,是浅拷贝,当要拷贝的数据是内置类型的时候进行按字节进行拷贝不会出错,但是如果要拷贝的数据类型是需要进行深拷贝的自定义类型的时候就会出错,而用for循环进行深拷贝不会出错

resize  :  调整大小

                                       //万能初始值
void resize(size_t n,const T& val=T())//val默认值怎么给---val接收T 类型的匿名对象 在这里内置类型也有(默任构造)函数     (前提是有模版)
{                                     // int i=0;  int j=int(); int k=int(1);   i--0  j--0  k--1if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n)//目的是为了初始化{*_finish = val;_finish++;}}
}
  • 如果 n 大于当前大小,则增加元素,并使用 val 初始化新增部分
  • 如果 n 小于当前大小,则通过调整 _finish 指针减少有效元素的数量
  • val默认值怎么给? ---val接收T 类型的匿名对象 在这里内置类型也有(默任构造)函数     (前提是有模版)   代码演试   :       // int i=0;  int j=int(); int k=int(1);   i==0  ,  j==0  , k==1

push_back : 添加元素

void push_back(const T& x)
{if (_finish == _end){size_t neew = capacity() == 0 ? 4 : 2 * capacity();reserve(neew);}*_finish = x;_finish++;//insert(end(), x);
}
  • 两种方法更推荐//insert(end(), x);

empty : 判空

bool empty()
{return _size == 0;
}
  •  empty() 判断容器是否为空

 pop_back : 后删

void pop_back()//后删
{assert(!empty());erase(--end());
}

 获取大小与容量 : size() , capacity() 

size_t capacity()const
{return _end - _start;
}size_t size()const
{return _finish - _start;
}
  • size() 返回当前有效元素的数量。
  • capacity() 返回当前分配的存储容量。

重载operator[] : 元素访问

T& operator[](size_t n)
{assert(n < size() && n >= 0);return _start[n];
}
const T& operator[](size_t n) const
{assert(n < size()&&n >= 0);return _start[n];
}
  • 提供了对元素的快速随机访问像访问数组一样 

insert : 插入元素

iterator insert(iterator pos, const T& x)//每个位置都是指针 iterator
{assert(pos >= _start&&pos<=_finish);if (_finish ==_end){size_t len = pos - _start;//记录一下pos的位置 pos之后有可能发生改变size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);pos = _start + len;//开空间的时侯,pos 有可能销毁掉,所以要换空间}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;
}
    • assert检查插入位置的有效性。
    • if判断存储空间,不足则调用 reserve 增加容量。
    • 从pos位置开始,将后续元素向后移动一个位置,留出插入的位置。
    • 将  x  放置在插入位置,并返回指向新元素的迭代器。

    erase:删除一个元素

    iterator erase(iterator pos)
    {assert(pos >= _start && pos < _finish);iterator it = pos + 1;while (it != _finish)//数据往后覆盖掉pos{*(it - 1) = *it;it++;}_finish--;return pos;
    }
    •  数据往后覆盖掉pos

    类的数据成员:

    private:iterator _start=nullptr;iterator _finish=nullptr;iterator _end=nullptr;
    
    • _start:指向空间的起始位置地址
    • _finish:指向有效数据的下一个位置的地址
    • _end:指向分配存储空间的末尾的地址,标志着存储的容量界限。

    ( 小编写代码的时侯为了方便把 _end_of_storge 换成了 _end ,不要学小编)

    自定义 vector 类的实现

    自己实现的vector类会和库里的vector重名,所以我们在vector.h中开辟一个我们自己的命名空间,在这个命名空间中用于我们vector的模拟实现

    namespace TXF
    {template <class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//typedef const T* iterator const;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin()const{return _start;}const_iterator end()const{return _finish;}//vector<int> v(10,1);10 是int 型更倾向于 InputIterator first 而不是size_t n;vector(size_t n,const T& val=T()){resize(n, val);}vector(int n, const T& val = T()){resize(n, val);}//[ first , last )template <class InputIterator>vector(InputIterator first,InputIterator last){while (first != last){push_back(*first);first++;}}vector(){}vector(const vector<T>& v)//拷贝构造{//way one:/*_start = new T[v.capacity()];for (size_t i = 0; i < se; i++){_start[i] = v._start[i];}_finish = _start + v.size();_end = _start + v.capacity();*///way two:reserve(v.capacity());for (auto e : v){push_back(e);}} void swap(vector<T>& v){std::swap(_start, v.start);std::swap(_finish, v._finish);std::swap(_end, v._end);}vector<T>& operator=(vector<T> v)//v相当于tmp{/*vector v1 = vector(v);return v1;*/swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _end = nullptr;}}void reserve(size_t n){if (n > capacity()){ size_t se = size();//要先保存size(),确保_finish 的位置T* tmp = new T[n];if (_start) {/*memcpy(tmp, _start, sizeof(T) * se);delete[] _start;*/   for (size_t i = 0; i < se; i++){tmp[i] = _start[i];}delete[]_start;}_start = tmp;//错误:_finish=_start+size();//size()是_finish-_start 而此时_start 已经改变_finish = _start + se;//所以要先保存原先的size()_end = _start + n;//_start 和_finish 和_end 都是 指针指向地址}}//万能初始值void resize(size_t n,const T& val=T())//val默认值怎么给---val接收T 类型的匿名对象 在这里内置类型也有(默任构造)函数     (前提是有模版){                                     // int i=0;  int j=int(); int k=int(1);   i--0  j--0  k--1if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n)//目的是为了初始化{*_finish = val;_finish++;}}}void push_back(const T& x){if (_finish == _end){size_t neew = capacity() == 0 ? 4 : 2 * capacity();reserve(neew);}*_finish = x;_finish++;//insert(end(), x);}bool empty(){return _size == 0;}void pop_back()//后删{assert(!empty());erase(--end());}size_t capacity()const{return _end - _start;}size_t size()const{return _finish - _start;}T& operator[](size_t n){assert(n < size() && n >= 0);return _start[n];}const T& operator[](size_t n)const{assert(n < size() && n >= 0);return _start[n];}iterator insert(iterator pos, const T& x)//每个位置都是指针 iterator{assert(pos >= _start&&pos<=_finish);if (_finish ==_end){size_t len = pos - _start;//记录一下pos的位置 pos之后有可能发生改变size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);pos = _start + len;//开空间的时侯,pos 有可能销毁掉,所以要换空间}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;}iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator it = pos + 1;while (it != _finish)//数据往后覆盖掉pos{*(it - 1) = *it;it++;}_finish--;return pos;}private:iterator _start=nullptr;iterator _finish=nullptr;iterator _end=nullptr;};


    总结

    以上是博客的全部内容, 小编希望对各位读者朋友们有帮助

    http://www.dtcms.com/a/287965.html

    相关文章:

  • 第J8周打卡
  • 【Linux】LVS(Linux virual server)环境搭建
  • uni-app 应用、页面、组件生命周期
  • Python+大模型 day02
  • 1.2M 小工具! 解决 Windows 系统疑难杂症
  • MVP 最小可行产品
  • 《Electron应用性能深耕:资源加载与内存治理的进阶路径》
  • 【51单片机学习】LED、独立按键
  • 一站式PDF转Markdown解决方案PDF3MD
  • Python技术题2
  • PostgreSQL常用命令与工具指南
  • 93.数字信号处理相关的一些问题
  • 【Java】【力扣】48.旋转图像
  • PyCharm + AI 辅助编程
  • 小明记账簿焕新记:从单色到多彩的主题进化之路
  • k8s快速部署(亲测无坑)
  • Go 语言核心机制深度剖析:指针、defer、多态与空接口实战指南
  • Windows 编程辅助技能:使用 MSDN
  • 千线万网,电路之行——LVS检查的内核逻辑
  • uniapp中腾讯地图SDK-安装及配置(自动定位回显城市)
  • 探索量子计算与法律理论的交叉领域
  • 智能体之变:深度解析OpenAI ChatGPT Agent如何重塑人机协作的未来
  • 文献阅读:全球农田的植被总初级生产力(GPP)、蒸散发(ET)和水分利用率(WUE)的变化研究
  • 周末总结(2024/07/19)
  • 若依部署项目到服务器
  • 数字图像处理(三:图像如果当作矩阵,那加减乘除处理了矩阵,那图像咋变):从LED冬奥会、奥运会及春晚等等大屏,到手机小屏,快来挖一挖里面都有什么
  • Springboot项目的搭建方式5种
  • 深入解析 Amazon Q:AWS 推出的企业级生成式 AI 助手
  • 默认显示两行文字,多余的文字省略掉,变成省略号
  • Vue状态管理:Vuex模块设计方案