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

C++篇 Vector模拟实现(1) 初始化 迭代器遍历 插入尾插尾删 一文详解

🎬 胖咕噜的稞达鸭:个人主页

🔥 个人专栏: 《数据结构》《C++初阶高阶》

⛺️技术的杠杆,撬动整个世界!

在这里插入图片描述
vector的使用
在学习vector之前必须了解vector文档中的相关描述;vector的介绍,这里我们挑重点的接口来进行阐述。
//注意:通篇都要看注释!精华总结感悟!!!

1.vector 的定义

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

2. vector iterator 的使用

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

3.vector 空间增长问题

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

4.vector 增删查改

vector增删查改接口说明
push_back尾插
pop_back尾删
find查找。(注意这个是算法模块实现,不是vector的成员接口)
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
operator[]像数组一样访问

vector初始化

vector<int>v1;//无参数的构造vector<int>v2(10, 1);//带参数的构造,初始化1个数组,vector<int>v3(++v2.begin(), --v2.end());//还可以用一段迭代器区间去初始化//v3用v2去初始化,不想要第一个,也不想用最后一个,可以向上面那么写。v3有8个1

vector遍历

	//遍历v3,下标第一种遍历方法for (size_t i = 0; i < v3.size(); i++){cout << v3[i] << " ";}cout << endl;//用迭代器去遍历,要指定类域,实例化的模板参数,第二种遍历vector<int>::iterator it = v3.begin();while (it != v3.end()){cout << *it << " ";it++;}cout << endl;//第三种遍历,范围forfor (auto e : v3){cout << e << " ";}cout << endl;

vector的扩容

void test_vector2()
{vector<int>v(10, 1);//初始化10个1v.reserve(20);//扩容到20cout << v.capacity() << endl;//20cout << v.size() << endl;//10v.reserve(15);cout << v.capacity() << endl;//20cout << v.size() << endl;//10v.reserve(5);cout << v.capacity() << endl;//20cout << v.size() << endl;//10    //坚决不缩容,capacity在增加到20之后,就没有变化了
}void test_vector3()
{vector<int>v(10.1);v.reserve(20);//扩容到capacity=20,size是10,capacity是20cout << v.capacity() << endl;//20cout << v.size() << endl;//10v.resize(15.2);//10个1,补了5个2(调试过程中),capacity=20cout << v.size() << endl;cout << v.capacity() << endl;v.resize(25.3);//capacity是30,按照2倍扩容,10个1,size=25cout << v.size() << endl;cout << v.capacity() << endl;v.resize(5);//capacity=30,size=5,只保留5个数据,5个1cout << v.size() << endl;cout << v.capacity() << endl;
}

vector遍历和流提取

void test_vector4()
{vector<int>v(10, 1);v.push_back(2);v.insert(v.begin(), 0);//遍历要用迭代器for (auto e : v){cout << e << " ";// 0  1  1  1  1  1   1   1   1   1   1   2}cout << endl;v.insert(v.begin() + 3, 10);//在第3个位置加一个10for (auto e : v){cout << e << " ";// 0  1  1  10  1   1  1   1   1   1   1   1   2}cout << endl;v.clear();//实现vector的流插入流提取,方法1int x;cin >> x;v.push_back(x);vector<int> v1(5, 0);//给5个数据初始化为0,方法2:用范围for进行遍历for (size_t i = 0; i < 5; i++){cin >> v1[i];//cin输入v1[i];}for (auto e : v1){cout << e << " ";//用范围for进行遍历,输入的数字用空格分割,最后打印出55个数字}cout << endl;
}

这里我们就有疑问了,string s2可不可以用vectorv2进行替代?
不可以! 1.string s2最后有\0,但是vectorv2没有\0
2.string可以支持比较大小,要按照ASCII码表进行比较,但是vector没有

vector的二维数组构建

void test_vector5()
{vector<string>v1;//vector中还可以存储自定义类型string s1("xxxxxxx");v1.push_back(s1);v1.push_back("yyyyyy");//const char* 还可以隐式类型转换for (const auto& e : v1)//*it取每个值,都是一个string,string给e,进行每次拷贝构造//有多少个string对象就要拷贝构造多少次,所以加& ,不改变遍历的值前面加const//以前都是拷贝一下Int,代价比较小,如果要拷贝string,不仅要开空间还要调用数据{cout << e << " ";}cout << endl;//假如要实现10*5的二维数组//vector<vector<int>>vv();//vector里面存vector,就是二维数组vector<int>v(5, 1);vector<vector<int>>vv(10, v);//10个v,v是v(5,1)//vv[2][1] = 2;//访问第二行第一个并且将其修改为2    ///看图!!!//编译器实例化了两个vector,vv.operator[](2).operator[](1) = 2;//等价于vv[2][1]=2;for (size_t i = 0; i < vv.size(); i++){for (size_t j = 0; j < vv[i].size(); j++){cout << vv[i][j] << " ";}cout << endl;}cout << endl;}

在这里插入图片描述

这里我们用C++做一道算法题来熟悉一下vector二维数组动态规划在算法题中的应用:杨辉三角OJ
在这里插入图片描述

clase Solution{
public:vector<vector<int>>generate(int numsRow){vector<vector<int>>vv(numsRow);//开n行for (size_t i = 0; i < numsRow; ++i){vv[i].resize(i + 1, 0);//第0行(索引)有1个,第1行(索引)有2个,每个数据默认给0//vv[i][0]=vv[i][vv[i].size()-1]=1;vv.front = vv.back = 1;}for (int i = 2; i < vv.size(); ++i)//第0行和第1行不需要{for (int j = 1; j < vv[i].size()-1; ++j)//避开每一行的第一个数据和组后一个数据{vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];//上一行的当前位置+上一行的前一个位置}}return vv;}
}

迭代器遍历,operator[]访问

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace Keda
{template<class T>class vector{public:typedef T* iterator;//使用typedef为T*(指向模板类型T的指针)定义了一个//别名iterator,用于遍历容器中的元素。表示vector类的迭代器是一个指向T类型的指针typedef const T* const_iterator;//const不可修改型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];}private:iterator _start = nullptr;iterator _finish = nullptr;//有效元素的末尾位置的下一个位置iterator _end_of_storage = nullptr;//用于判断是否需要扩容,意为空间用完了};
}

vector模板

//模板
template<class T>
void print_vector(const vector<T>& v)//const对象就要用const迭代器
{//在没有实例化的类模板里面取东西,规定:没有实例化的类模板里面取东西//编译器不能区分这里的const_iterator是类型还是静态成员变量//typename vector<T>::const_iterator it = v.begin();//也可以直接由编译器自己推导//静态成员变量不需要有类型auto it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;for (auto e : v){cout << e << " ";}cout << endl;
}

vector插入!!!(内设及迭代器失效问题!!必看)

void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//开新的空间memcpy(tmp, _start, size() * sizeof(T));//拷贝旧空间的数据给新的空间delete[]_start;//释放掉旧的空间_start = tmp;//指向新的空间_finish = tmp + old_size;_end_of_storage = tmp + n;}}void push_back(const T& x)
{if (_finish != _end_of_storage){*_finish = x;++_finish;}else{reserve(capacity() == 0 ? 4 : capacity() * 2);*_finish = x;++_finish;}iterator insert(iterator pos, const T& x){if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = len + _start;}iterator end = _finish - 1;while (end >= pos)//可以等于0{*(end + 1) = *(end);--end;}*pos = x;++_finish;return pos;
}

这里会出现迭代器失效的问题:
分析:当需要插入一个新的x,很有可能出现_finish==_end_of_storage的情况,需要扩容开辟一个新的tmp指针指向一块新的空间,然后释放掉旧的_start,让tmp所在的这块空间称为新_start,然后构建好新的_finish,_end_of_storage
为什么:但是!!!!!pos还指向的是旧_start,扩容的时候如果没有考虑到pos,释放旧的_start的时候会将pos一并释放,所以再插入这里使用的pos是一个野指针
解决办法就是找到新的_start中对应旧_start的pos位置
所以在扩容的地方算出相对len,size_t len=pos-_start;扩容好了之后更新pos即可: pos=len+_start;
在这里插入图片描述
续:测试插入

void test_vector2()
{vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//v.push_back(5);print_vector(v);v.insert(v.begin() + 3, 45);print_vector(v);//迭代器区间要求传左闭右开int x;cin >> x;auto pos = find(v.begin(), v.end(), x);if (pos != v.end()){//v.insert(pos, 40);//查找x,找到之后让pos*=10,假如pos=2,我们乘等10,应该输出20//(*pos) *= 10;//实际上输出400,为什么?这里也是迭代器失效的问题,所以insert以后pos就失效了,不要访问//pos还指向的是旧空间,再解引用就是越界的野指针,即使在insert里面做了改变,已经更新过了,但是毕竟是在内部,实参的改变不影响形参//那要是一定要哦访问呢???//这里可以将insert操作完之后在内部操作的值pos返回,iterator insert (iterator pos,const T& s); 下面return pos//在测试中:pos=v.insert(pos,40); (*(p+1)) *=10pos = v.insert(pos, 40);(*(pos+1) ) *= 10;}print_vector(v);}

这里也是迭代器失效的问题,insert以后pos就失效了,不要访问;
pos还指向的是旧空间,再解引用就是越界的野指针,即使在insert里面做了改变,已经更新过了,但是毕竟是在内部,实参的改变不影响形参(内部的更新不影响外部)

vector尾删

bool empty()
{return _start == _finish;
}
void pop_back()
{assert(!empty());--_finish;
}
http://www.dtcms.com/a/418335.html

相关文章:

  • 学习日报 20250928|React 中实现 “实时检测”:useEffect 依赖项触发机制详解
  • 怎么样可以做自己的网站做网站投注代理犯罪吗
  • 网站空间是服务器吗成都网站设计建设推荐
  • 基于 LangGraph 框架实现智能研究助手示例程序
  • 常用网络命令
  • 实验指导-基于阿里云函数计算的简单邮件发送服务 之数据库访问中间件
  • PPO算法
  • 网站建设公司方维wordpress 上传文件路径
  • gRPC0到1系列之【6】
  • 【Java系列课程·Java学前须知】第3课 JDK,JVM,JRE的区别和优缺
  • JVM栈溢出时如何dump栈信息?
  • 重庆奉节网站建设公司重庆沙坪坝地图全图
  • RK3588芯片与板卡全面解析:旗舰级AIoT与边缘计算的核心
  • 226.翻转二叉树(二叉树算法题)
  • #itertools.product
  • AcWing 1172:祖孙询问 ← 倍增法求LCA(DFS预处理)
  • C语言 分支结构(1)
  • 扭蛋机抽赏小程序:重构线上娱乐的“盲盒式”新体验
  • EtherNet/IP转EtherCAT网关在新能源制造中实现机器人与运动卡数据互通
  • Imatest-Wedge模块
  • 岳阳博物馆网站网站建设想法
  • Day03_STM32F103C8T6学习笔记6-9章(江科大)
  • 专业企业网站搭建服务docker创建wordpress
  • 各大网站的网址网站的功能性
  • ZYNQ平台摄像头性能深度对决:OV7725 vs OV5640全面速度测试与优化实战
  • Qt 界面优化 --- 绘图
  • MySQL Online DDL:高性能表结构变更指南
  • 操作系统:进程调度,创建和终止
  • Kafka09-速答-尚硅谷
  • Jenkins与GitLab-CI的技术对比分析