stl_vector类(使用+实现)(C++)

vector

  • 一、vector-简单介绍
  • 二、vector的常用接口
    • 1.常见构造
    • 2.iterator的使用
    • 3.容量操作
    • 4.增删查改操作
    • 5.迭代器失效问题
    • 6.动态二维数组
  • 三、vector实现
    • 1.vector类重要的方法实现分析介绍
      • (1)、涉及memcpy深浅拷贝问题
      • (2)、成员变量
    • 2.vector类整体实现代码
  • 四、vector< char > 和 string区别

一、vector-简单介绍

Vector是一个封装了动态大小数组的顺序容器(Sequence Container)。可以简单的认为,vector是一个能够存放任意类型的动态数组。
vector是一个类模板,其中封装了类的多种方法。例如,在使用时要改变大小,它的大小容器会自动处理。字符串的增删查改等操作,通过调用类中的方法便捷的进行操作…。

二、vector的常用接口

1.常见构造

(constructor)构造函数声明接口说明
vector()无参构造
vector(size_t n, const T& val = T()构造并初始化n个val
vector(const vector& x)拷贝构造
vector(InputIterator first, InputIterator last使用迭代器进行初始化构造

test:

void test_vector_constructor()
{//因为vector是一个模板类,所以string也可以当参数的类型//无参构造vector<string> v1;string name("ren");//正常尾插一个string类对象v1.push_back(name);//匿名对象v1.push_back(string("yv"));//隐式类型转换v1.push_back("qing");//构造并初始化n个valvector<string> v2(5, "xxx");//拷贝构造vector<string> v3(v1);//使用迭代器初始化构造string str("hello world");vector<char> v4(str.begin(), str.end());vector<int> v5(str.begin(), str.end());//用char初始化int会有整型提升,类型转换。  ASCII//使用数组构造int a[] = { 16,2,77,29 };vector<int> v6(a, a + 4);  //a本质是指针
}

模板类参数的底层介绍

2.iterator的使用

iterator的使用接口说明
begin + end获取第一个数据位置的iterator/const_iterator,获取最后一个数据的下一个位置的iterator/const_iterator
rbegin + rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator

test:
注意: 在string中说过,迭代器实现了之后,范围for可以直接使用。范围for本质就是迭代器

void test_iterator()
{int a[] = { 16, 2, 77, 239, 23, 3, 43, 2, 3, 3, };int size = sizeof(a) / sizeof(a[0]);vector<int> v1(a, a + size);vector<int>::iterator it = v1.begin();while (it != v1.end()){cout << *it << " ";++it;}cout << endl;vector<int>::reverse_iterator rit = v1.rbegin();while (rit != v1.rend()){cout << *rit << " ";++rit;}cout << endl;
}

iterator指向

3.容量操作

函数名称接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
reserve改变vector的capacity
resize改变vector的size

reserve负责开空间,如果预先知道需要使用开多少空间,reserve可以缓解vector增容的代价。

test:

void test_space()
{vector<int> v1;v1.resize(10);for (size_t i = 0; i < 10; i++){//使用[]改变v1[i] = i;}for (auto e : v1){cout << e << " ";}cout << endl;cout << " size:" << v1.size() << endl;cout << "capacity:" << v1.capacity() << endl;cout << "empty:" << v1.empty() << endl << endl;vector<int> v2;v2.reserve(10);for (size_t i = 0; i < 5; i++){//尾插v2.push_back(i);}for (auto e : v2){cout << e << " ";}cout << endl;cout << " size:" << v2.size() << endl;cout << "capacity:" << v2.capacity() << endl;v2.clear();cout << "empty:" << v2.empty() << endl;
}

test(vector默认扩容机制):vector的增容是1.5倍

void test_vector_expand()
{size_t sz;vector<int> v;sz = v.capacity();for (size_t i = 0; i < 100; i++){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "New capacity:" << sz << endl;}}
}

4.增删查改操作

函数名称接口说明
push_back尾插
pop_back尾删
erase删除pos位置的数据
insert在pos之前插入val
swap交换两个vector的数据空间
operator[]像数组一样访问

test:

void test_modifiers()
{vector<int> v;//push_backv.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);for (auto e : v){cout << e << " ";}cout << endl;//删除操作v.pop_back();v.erase(v.begin());v.erase(v.end() - 1);    //vector可以直接减1,list不行for (auto e : v){cout << e << " ";}cout << endl;//insertv.insert(v.begin(), 3);v.insert(v.end(), 10);for (auto e : v){cout << e << " ";}cout << endl;//swapvector<int> v1;v1.swap(v);    //v现在没有值for (auto e : v1){cout << e << " ";}cout << endl;for (size_t i = 0; i < v1.size(); i++){//operatorv1[i]++;cout << v1[i] << " ";}cout << endl;
}

5.迭代器失效问题

引起底层空间改变的操作,都有可能是迭代器失效
这里使用insert和erase举例,当然别的方法也存在这个情况,例如:resize,reserve,push_back…。

解决迭代器失效的方法:在使用前,对迭代器重新赋值即可。

test:

void test_iterator()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout << e << " ";}cout << endl;//vector<int>::iterator it = v.begin();//v.insert(it, 100);//高危行为//*it += 10;//删除pos位置的值,导致迭代器失效/*vector<int>::iterator pos = find(v.begin(), v.end(), 3);v.erase(pos);cout << *pos << endl;*///如何解决问题:对迭代器重新赋值//eg:删除所有偶数vector<int>::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0){//因为erase在调用完成后返回pos的后一个位置,//如果pos是最后一个位置则这是容器结束it = v.erase(it);}}
}

迭代器失效

6.动态二维数组

动态二维数组

三、vector实现

1.vector类重要的方法实现分析介绍

(1)、涉及memcpy深浅拷贝问题

在构造函数里涉及到自定义类型元素中的资源管理。
如果在vector< string >类的单参构造中,使用memcpy。如果仅仅对自定义类型的元素进行拷贝完全没问题,但是拷贝的自定义类型元素中涉及到资源管理就会出现问题。

  1. 内存泄漏
  2. 多次释放一块地址,野指针。程序崩溃。

深拷贝问题
以拷贝构造举例,这里就不能使用memcpy了,而是一步步遍历赋值

	vector(const vector<T>& v){_start = new T[v.capacity()];//如果是vector<string> 在拷贝时需要是深拷贝//memcpy(_start, v._start, sizeof(T) * v.size());for (size_t i = 0; i < v.size(); i++){//_start[i] = v._start[i];_start[i] = T(v._start[i]);}_finish = _start + v.size();_end_of_storage = _start + v.capacity();}

所以在实现单参构造函数时,不能使用memcpy

(2)、成员变量

在vector类实现中,我们采用和库中相同的成员变量,三个迭代器,在前面的动态二维数组知识点提到。

	private://这里最好给默认值,这样在构造函数就可以不用刻意初始化为nullptr//使用未定义的值进行构造会出现大问题iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;

_finish - _start = size();
_end_of_storage - _start = capacity();

2.vector类整体实现代码

namespace kpl
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;// 无参构造vector()//: _start(nullptr)     //这里都可以省略,因为在成员变量声明的时候给了默认值nullptr//, _finish(nullptr)//, _endOfStorage(nullptr){}vector(size_t n, const T& value = T()){/*reserve(n);while (n--){push_back(value);}*/resize(n, value);}//vector<int> v(10, 1);//vector<int> v(10u, 1);//vector<string> v(10, "11111");//解决例子匹配问题vector(int n, const T& value = T()){resize(n, value);}// 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器// 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}//拷贝构造vector(const vector<T>& v){_start = new T[v.capacity()];//如果是vector<string> 在拷贝时需要是深拷贝//memcpy(_start, v._start, sizeof(T) * v.size());for (size_t i = 0; i < v.size(); i++){//_start[i] = v._start[i];_start[i] = T(v._start[i]);}_finish = _start + v.size();_endOfStorage  = _start + v.capacity();}//运算符重载,现代写法void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage , v._endOfStorage );}vector<T>& operator=(vector<T> v){//给成员变量都初始化成nullptr的好处就体现出来了,如果交换之后是一个随机的地址就会出现错误swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _endOfStorage = nullptr;}}// 迭代器相关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 _endOfStorage - _start; }bool empty() const { return _start == _finish; }void reserve(size_t n){if (n > capacity()){size_t sz = size();// 1. 开辟新空间T* tmp = new T[n];// 2. 拷贝元素if (_start){for (size_t i = 0; i < sz; ++i)tmp[i] = _start[i];// 3. 释放旧空间delete[] _start;}_start = tmp;//这里不能使用size(),因为size的实现是_finish-_start//这时_start改变了,但是finish还没改变_finish = _start + sz;_endOfStorage = _start + n;}}void resize(size_t n, const T& value = T()){if (n <= size()){_finish = _start + n;return;}else{//增容reserve(n);//赋值while (_finish != _start + n){*_finish = value;++_finish;}}}// 元素访问T& operator[](size_t pos) { assert(pos < size());return _start[pos]; }const T& operator[](size_t pos)const { assert(pos < size());return _start[pos]; }T& front(){return *_start;}const T& front()const{return *_start;}T& back(){return *(_finish - 1);}const T& back()const{return *(_finish - 1);}// vector的修改操作void push_back(const T& x) { //if (_finish == _endOfStorage)//{//	size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;//	reserve(newcapacity);//}//*_finish = x;//++_finish;insert(end(), x); }void pop_back() { erase(end() - 1); }//vector erase和insert迭代器对象使用后,不能在访问这个迭代器//我们认为它失效了,访问的结果是未定义的iterator insert(iterator pos, const T& x){assert(pos <= _finish && pos >= _start);// 空间不够先进行增容if (_finish == _endOfStorage){//保存pos的相对位置size_t len = pos - _start;size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;reserve(newCapacity);//解决pos迭代器失效问题,对pos位置更新pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}// 返回删除数据的下一个数据,如果是最后一个元素,则是容器结束// 方便解决:一边遍历一边删除的迭代器失效问题iterator erase(iterator pos){assert(pos < _finish && pos >= _start);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}--_finish;return pos;}private:iterator _start = nullptr;		// 指向数据块的开始iterator _finish = nullptr;		// 指向有效数据的尾iterator _endOfStorage = nullptr;  // 指向存储容量的尾};
}

四、vector< char > 和 string区别

  1. 结构不同,string受’\0’的影响,并且要和C语言兼容
  2. string接口丰富,有很多专用接口。eg:+=, find…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/75045.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LUN映射出错导致写操作不互斥的服务器数据恢复案例

服务器数据恢复环境&#xff1a; 某公司的光纤SAN存储系统&#xff0c;6块硬盘组建一组RAID6&#xff0c;划分若干LUN&#xff0c;MAP到不同的SOLARIS操作系统服务器上。 服务器故障&分析&#xff1a; 由于业务增长需要新增应用&#xff0c;工作人员增加了一台IBM服务器&am…

uniapp小程序,根据小程序的环境版本,控制的显页面功能按钮的示隐藏

需求&#xff1a;根据小程序环境控制控制页面某个功能按钮的显示隐藏&#xff1b; 下面是官方文档和功能实现的相关代码&#xff1a; 实现上面需要&#xff0c;用到了uni.getAccountInfoSync()&#xff1a; uni.getAccountInfoSync() 是一个 Uniapp 提供的同步方法&#xff0c…

ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域的数据分析

一、 空间数据获取与制图 1.1 软件安装与应用讲解 1.2 空间数据介绍 1.3海量空间数据下载 1.4 ArcGIS软件快速入门 1.5 Geodatabase地理数据库 二、 ArcGIS专题地图制作 2.1专题地图制作规范 2.2 空间数据的准备与处理 2.3 空间数据可视化&#xff1a;地图符号与注记 …

【图论】差分约束

一.情景导入 x1-x0<9 ; x2-x0<14 ; x3-x0<15 ; x2-x1<10 ; x3-x2<9; 求x3-x0的最大值&#xff1b; 二.数学解法 联立式子2和5&#xff0c;可得x3-x0<23;但式子3可得x3-x0<15。所以最大值为15&#xff1b; 三.图论 但式子多了我们就不好解了&#xff0…

深入探索Vue.js核心技术与跨平台开发uni-app实战

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 前言 在当今Web应用不断…

TensorFlow项目练手(三)——基于GRU股票走势预测任务

项目介绍 项目基于GRU算法通过20天的股票序列来预测第21天的数据&#xff0c;有些项目也可以用LSTM算法&#xff0c;两者主要差别如下&#xff1a; LSTM算法&#xff1a;目前使用最多的时间序列算法&#xff0c;是一种特殊的RNN&#xff08;循环神经网络&#xff09;&#xf…

Safari 查看 http 请求

文章目录 1、开启 Safari 开发菜单2、显示 JavaScript 控制台 1、开启 Safari 开发菜单 Safari 设置中&#xff0c;打开开发菜单选项 *** 选择完成后&#xff0c;Safari 的目录栏就会出现一个 开发 功能。 2、显示 JavaScript 控制台 开启页面后&#xff0c;在开发中选中 显…

规划路线(微信小程序、H5)

//地图getLocationDian(e1, e2) {console.log(e1, e2);let self this;self.xx1 [];self.xx2 [];self.points [];// self.markers[]console.log(self.markers, >marks);// self.$jsonp(url, data).then(re > {// var coors re.result.routes[0].polyline;// for (v…

Electron逆向调试

复杂程序处理方式&#xff1a; 复杂方式通过 调用窗口 添加命令行参数 启动允许调用&#xff0c;就可以实现调试发布环境的electron程序。 断点调试分析程序的走向&#xff0c;程序基本上会有混淆代码处理&#xff0c; 需要调整代码格式&#xff0c;处理程序。

ARM 常见汇编指令学习 9 - 缓存管理指令 DC 与 IC

文章目录 ARM64 DC 与 IC 指令 上篇文章&#xff1a;ARM 常见汇编指令学习 8 - dsb sy 指令及 dsb 参数介绍 ARM64 DC 与 IC 指令 AArch64指令集中有两条关于缓存维护&#xff08;cache maintenance&#xff09;的指令&#xff0c;分别是IC和DC。 IC 是用于指令缓存操作&…

数智保险 创新未来 | GBASE南大通用亮相中国保险科技应用高峰论坛

本届峰会以“数智保险 创新未来”为主题&#xff0c;GBASE南大通用携新一代创新数据库产品及金融信创解决方案精彩亮相&#xff0c;与国内八百多位保险公司高管和众多保险科技公司技术专家&#xff0c;就保险领域数字化的创新应用及生态建设、新一代技术突破及发展机遇、前沿科…

Git下:Git命令使用-详细解读

今天给大家讲一讲 Git常用命令的使用说明&#xff0c;希望本篇文章对大家有所帮助。 一、Git 安装 Git 的详细安装教程&#xff1a;见上一篇文章《Git上&#xff1a;Git安装教程》&#xff1a; Git上&#xff1a;全网最全最详细的Git安装教程&#xff0c;建议收藏保存 二、…

2023,谁在引领实时互动进入高清时代?

实践是检验真理的唯一标准&#xff0c;技术是行业进步的核心动能。在实时互动的新时代里&#xff0c;不断进化的声网已然完成自证。 作者|斗斗 出品|产业家 “一个医疗行业的客户&#xff0c;曾向我们提出一个需求&#xff0c;希望在120急救场景下&#xff0c;可以远程看清…

.net 6 efcore一个model映射到多张表(非使用IEntityTypeConfiguration)

现在有两张表&#xff0c;结构一模一样&#xff0c;我又不想创建两个一模一样的model&#xff0c;就想一个model映射到两张表 废话不多说直接上代码 安装依赖包 创建model namespace oneModelMultiTable.Model {public class Test{public int id { get; set; }public string…

两数相加 II

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数字都不会以零开头。 示例1&#xff1a; 输入&#xff1a;l1 [7,2,4,3], l2 [5,6,4] 输…

React Native元素旋转一定的角度

mMeArrowIcon: {fontSize: 30, color: #999, transform: [{rotate: 180deg}]},<Icon name"arrow" style{styles.mMeArrowIcon}></Icon>参考链接&#xff1a; https://reactnative.cn/docs/transforms https://chat.xutongbao.top/

子组件未抛出事件 父组件如何通过$refs监听子组件中数据的变化

我们平时开发项目会使用一些比较成熟的组件库, 但是在极小的情况下,可能会出现我们需要监听某个属性的变化,使我们的页面根据这个属性发生一些改变,但是偏偏组件库没有把这个属性抛出来,当我们使用watch通过refs监听时,由于生命周期的原因还不能拿到,这时候我们可以这样做,以下…

《TCP IP 网络编程》第十五章

第 15 章 套接字和标准I/O 15.1 标准 I/O 的优点 标准 I/O 函数的两个优点&#xff1a; 除了使用 read 和 write 函数收发数据外&#xff0c;还能使用标准 I/O 函数收发数据。下面是标准 I/O 函数的两个优点&#xff1a; 标准 I/O 函数具有良好的移植性标准 I/O 函数可以利用…

安卓:BottomNavigationBar——底部导航栏控件

目录 一、BottomNavigationBar介绍 二、BottomNavigationBar的常用方法及其常用类 &#xff08;一&#xff09;、常用方法 1. 添加菜单项 2. 移除菜单项 3. 设置选中监听器 4. 设置当前选中项 5. 设置徽章 6. 样式和颜色定制 7. 动画效果 8. 隐藏底部导航栏。 9、设…

过程:从虚拟机上添加 git 并成功提交到 GitLab 的全过程

Ⅰ、准备工作&#xff1a; 1、Git 查看&#xff1a; 其一、命令&#xff1a;git --version // 此时就能在虚拟机环境下看到 git 的版本为: git version 2.41.0 其二、如何在虚拟机上安装 git &#xff1a; A、命令 &#xff1a; sudo apt-get install git B、然后再输入虚…