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

vector的简单实现及常用接口

目录

1.vector类的介绍

2.vector类常用的接口

2.1 vector类常用构造函数

2.2 vector类常用迭代器

2.3 vector类空间接口

2.4 vector的增删查改

3.vector类的简单实现

3.1 vector类结构以及构造及析构实现

3.2 string类常用接口实现

4.迭代器失效

5.insert的修改


1.vector类的介绍

vector类代表的是大小可以变化的数组,常见的有,增,删,查,改几个接口(功能),以及vector迭代器的使用。

2.vector类常用的接口

2.1 vector类常用构造函数

构造函数接口说明
vector()无参构造
vector(size_t n,const T& val=T())(注:T代表模版)构造并初始化n个val
vector(const vector<T>& v)拷贝构造
vector (InputIterator first, InputIterator last)
使用迭代器进行初始化构

#include<vector>
#include<iostream>
#include<string>
using namespace std;
int main()
{vector<int> v;//无参构造vector<int> v1(10, 1);//初始化10个1vector<int> v2(v1);//拷贝构造string s;vector<int> v3(s.begin(), s.end());//利用迭代器初始化return 0;
}

vector类使用时一定要实例化

2.2 vector类常用迭代器

iterator的使用接口说明
begin+end
begin获取第一个数据位置的iterator/const_iterator
end获取最后一个数据的下一个位置的iterator/const_iterator
rbegin+rend
begin获取最后一个数据位置的iterator/const_iterator
end获取第一个数据的前一个位置的iterator/const_iterator
#include<vector>
#include<iostream>
using namespace std;
int main()
{vector<int> myvector;//插入数据for (int i = 1; i <= 5; i++) myvector.push_back(i);//正向迭代器for (vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it)cout << ' ' << *it;cout << '\n';//反向迭代器for (vector<int>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); ++it)cout << ' ' << *it;cout << '\n';return 0;
}

2.3 vector类空间接口

容量空间接口说明
size
获取数据个数
capacity
获取容量大小
empty
判断是否为空
resize
改变vectorsize
reserve
改变vectorcapacity

注:vscapacity是按1.5倍增长的,g++是按2倍增长的

reserve(size_t res_arg=0) 预留空间,不改变有效元素个数,当 reserve 的参数小于 string 的底层空间总大小时, reserver 不会改变容量大小。

resize(size_t n) 与 resize(size_t n,T val=T()) 都是将vector有效数据改变到 n 个,不同的是当n>capacity时 resize(n) 用 val来填充多出的元素空间,若没有写val的参数,使用val类型的默认构造的缺省值
注意: resize 在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

#include<vector>
#include<iostream>
#include<string>
using namespace std;
template<class T>
void Print(const vector<T>& x)
{for (auto e : x){cout << e << " ";}cout << endl;
}
int main()
{vector<int> v(10, 1);cout << v.size() << " " << v.capacity() << " ";Print(v);//打印v.resize(5);//当n小于capacitycout << v.size() << " " << v.capacity() << " ";Print(v);v.resize(15, 2);//当n大于capacity,多的空间用val填入cout << v.size() << " " << v.capacity() << " ";Print(v);v.resize(25);//使用val类型的默认构造的缺省值cout << v.size() << " " << v.capacity() << " ";Print(v);v.reserve(20);//当n小于capacity时,不变cout << v.size() << " " << v.capacity() << " ";Print(v);v.reserve(30);cout << v.size() << " " << v.capacity() << " ";Print(v);return 0;
}

当确定vector存储元素的个数时,可以利用reserve提前开好一定的空间,防止多次扩容

2.4 vector的增删查改

函数名称接口说明
push_back尾插
pop_back()尾删
insert在pos位置前插入数据
erase删除pos位置的数据
swap交换两个vector的存储空间
operator[ ]和数组一样访问
#include<vector>
#include<iostream>
#include<string>
using namespace std;
template<class T>
void Print(const vector<T>& x)
{for (auto e : x){cout << e << " ";}cout << endl;
}
int main()
{vector<int> v;//尾插v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);Print(v);//尾删v.pop_back();v.pop_back();Print(v);//插入v.insert(v.begin()+2, 20);//第一个参数是迭代器类型Print(v);//删除v.erase(v.end()-1);//参数是迭代器类型Print(v);//数组访问v[1] = 10;Print(v);//交换数据vector<int> v1(10, 1);Print(v);Print(v1);v.swap(v1);Print(v);Print(v1);return 0;
}

3.vector类的简单实现

3.1 vector类结构以及构造及析构实现

vector内的数据类型可能是int,double,对于这种不确定是那种类型时,可以利用模版来解决,而vector的结构由三个迭代器组成,而迭代器的底层就是指针

namespace chuxin
{template<class T>class vector{public:typedef const T* const_iterator;typedef T* iterator;private:iterator _start=nullptr;iterator _finish=nullptr;iterator _end_of_storage=nullptr;};
}

对于默认构造,由于此时给了成员变量的缺省值,而且默认构造要走初始化列表,默认构造就不需要写任何内容

对于用迭代器来构造vector,可以利用模版,类模板的成员函数,还可以继续是函数模版

vector()//默认构造
{}
vector(const vector<T>& v)//拷贝构造函数
{reserve(v.size());//开辟空间for (auto e : v){push_back(e);//一个一个数据插入}
}
// 类模板的成员函数,还可以继续是函数模版
//利用迭代器来构造
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}
//构造并初始化
vector(size_t n, const T& val = T())//T()代表用T类型的默认构造来做缺省值
{reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}
}
~vector()//析构
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
}

3.2 string类常用接口实现

4.2.1 size,capacityt ,operator[ ],clear,begin,end,赋值=

size,capacityt ,operator[ ],clear,begin,end这几个比较简单,不做叙述

	iterator begin()//迭代器{return _start;}iterator end(){return _finish;}const_iterator begin()const{return _start;}const_iterator end()const{return _finish;}size_t size()const{return _finish - _start;}size_t capacity()const{return _end_of_storage - _start;}T& operator[](size_t i){assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}void clear()//清理数据{_finish = _start;}

对于赋值=,可以利用swap交换来使用

void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}
vector<T>& operator=(vector<T>& v)//赋值
{swap(v);return *this;
}

3.2.2 reserse(扩容)

对于扩容,这里有一些关于深浅拷贝的问题,来看一下代码

void reserve(size_t n)//扩容
{if (n > capacity()){size_t old_size = _finish - _start;//记录数据iterator tmp = new T[n];memcpy(tmp, _start, old_size * sizeof(T));_start = tmp;_finish = tmp + old_size;_end_of_storage=tmp + n;}
}
memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中,如果拷贝的是内置类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,也就是说,当使用memcap拷贝数据时,_start指向的空间和tmp指向的空间就是同一块地方
void reserve(size_t n)//扩容
{if (n > capacity()){size_t old_size = _finish - _start;//记录数据iterator tmp = new T[n];for (size_t i = 0; i < old_size; i++)//深拷贝{tmp[i] = _start[i];}_start = tmp;_finish = tmp + old_size;_end_of_storage=tmp + n;}
}

3.2.3 empty,pop_back,push_back

bool empty()const
{return _finish == _start;
}
void pop_back()//尾删
{assert(_finish != _start);--_finish;
}
void push_back(const T& x)//尾插
{if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;
}

3.2.4 insert

iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}iterator end = _finish - 1;while (pos <= end){*(end + 1) = *end;//移动数据--end;}*pos = x;++_finish;return pos;
}

这里的insert有些问题,下文解释

3.2.5 erase

void erase(iterator pos)
{assert(pos < _finish);assert(pos >= _start);iterator it = pos + 1;while (it != end()){*(it-1) = *it;++it;}--_finish;
}

4.迭代器失效

迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,或者迭代器与预想的作用不同

来看下列代码

void test_vector1()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);Print(v);//打印int x;cin >> x;auto p = find(v.begin(), v.end(), x);if (p != v.end()){v.insert(p, 20);(*p) *= 10;}Print(v);
}

p原本指向的是2,当插入20时,p的指向确变成了插入的数据,此时p的位置没有改变,这就是一个迭代器失效,因此insert以后p就是失效,不要直接访问,要访问就要更新这个失效的迭代器的值

	void test_vector1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);Print(v);int x;cin >> x;auto p = find(v.begin(), v.end(), x);if (p != v.end()){// insert以后p就是失效,不要直接访问,要访问就要更新这个失效的迭代器的值/*v.insert(p, 20);(*p) *= 10;*/p = v.insert(p, 40);(*(p + 1)) *= 10;}Print(v);}
}

同时当插入一个数时,进行了扩容,由于旧空间会被释放,此时的迭代器由于没有更新,变成了野指针,不能直接使用

void test_vector1()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(4);v.push_back(5);auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)//删除偶数v.erase(it);++it;}prinCon(v);//打印
}

对于这份代码,发现连续的偶数无法删除,当遇到偶数时,删除,此时迭代器失效,没有改变位置,对迭代器++,就会跳过连续的偶数,如果pos刚好是最后一个元素,删完之后pos刚好是end 的位置,而end位置是没有元素的,那么pos同样也失效了,因此可以对迭代器进行更新

void test_vector1()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(4);v.push_back(5);auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)it = v.erase(it);else++it;}prinCon(v);
}

5.insert的修改

由于知道扩容会对迭代器失效,在某一位置插入数据时,可以记录一下迭代器,避免迭代器失效

iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;//更新迭代器}iterator end = _finish - 1;while (pos <= end){*(end + 1) = *end;//移动数据--end;}*pos = x;++_finish;return pos;
}


文章转载自:
http://affectlessness.isnyv.cn
http://acheb.isnyv.cn
http://careenage.isnyv.cn
http://adoptability.isnyv.cn
http://alligatorfish.isnyv.cn
http://autarchical.isnyv.cn
http://astatki.isnyv.cn
http://appreciatory.isnyv.cn
http://capsid.isnyv.cn
http://beguine.isnyv.cn
http://augean.isnyv.cn
http://bilingual.isnyv.cn
http://approbatory.isnyv.cn
http://arsenotherapy.isnyv.cn
http://atraumatically.isnyv.cn
http://alphabetically.isnyv.cn
http://achromobacter.isnyv.cn
http://antipollution.isnyv.cn
http://barmy.isnyv.cn
http://chlorophyl.isnyv.cn
http://childishly.isnyv.cn
http://aerographer.isnyv.cn
http://chairlady.isnyv.cn
http://aerobiological.isnyv.cn
http://abuttals.isnyv.cn
http://carbonara.isnyv.cn
http://astrology.isnyv.cn
http://cacique.isnyv.cn
http://arbitrative.isnyv.cn
http://arrect.isnyv.cn
http://www.dtcms.com/a/280975.html

相关文章:

  • I/O 多路复用详解笔记
  • 笔试——Day8
  • CentOS 7 Linux 离线安装 docker-compose
  • 【PTA数据结构 | C语言版】层序遍历二叉树
  • SQLlite下载以及简单使用
  • AI创作系列第19篇:海狸IM 20250714版本重磅升级 - 移动端UI全面焕新
  • linux的磁盘满了清理办法
  • 图机器学习(7)——图神经网络 (Graph Neural Network, GNN)
  • 【10】如何对图像进行分割(下)
  • 删除k8s卸载后残留挂载点目录
  • 【群晖NAS】云服务器与群晖NAS(无公网)的FRP内网穿透之旅
  • Kimi K2 替换 Claude Code 默认模型
  • AI-Compass Embedding模型模块:15+主流向量化技术的多模态语义表示生态,涵盖文本图像音频嵌入、RAG检索增强、向量数据库集成与工程化实践
  • 进程创建与退出的原理
  • 5.数据归一化
  • Paimon 删除向量
  • 元宇宙经济:虚实交融下的数字文明新范式
  • Python 函数:从“是什么”到“怎么用”的完整指南
  • 【Linux驱动-快速回顾】一文快速理解GIC内部寄存器对中断的控制
  • Claude技术全景解读:从安全聊天机器人到自主智能体的演进之路
  • 数据结构自学Day7-- 二叉树
  • 项目总体框架(servlet+axios+Mybatis)
  • ue4 houdini pivot painter 学习笔记
  • 可微分3D高斯溅射(3DGS)在医学图像三维重建中的应用
  • OpenCV 对数变换函数logTransform()
  • ubuntu22.04 软创建 RAID1 与配置流程
  • pytest快速上手指南【pytest】
  • LED 照明应用提供高性价比方案?会是你的首选吗?
  • C++ 中两个类之间的通信方式
  • labview关于OOP