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

STL之vector容器

vector的介绍

1.vector是可变大小数组的容器

2.像数组一样,采用连续的空间存储,也就意味着可以通过下标去访问,但它的大小可以动态改变

3.每次的插入都要开空间吗?开空间就要意味着先开临时空间,然后在拷贝旧的到新的上面,在释放原来的,就时间而言,这是一个相对代价很高的任务;显然这是不行的,vector的分配策略:会分配一些而外的空间以适应可能的增长,例如我们平时说的以1.5或者2倍的方式增容,不同的库选择不同的策略权衡空间的使用和重新分配,以至于在插入一个元素时能在常数时间内插入;

vector的使用

构造函数:

 

注意:这里的allocator_type是空间配置器,与内存池相关,可以暂时忽略

          这里的vector<int>要传模板,指明你每个元素里面存的类型是什么 

第一个能构造一个空的vector数组

第二个能构造4个元素,每个元素里面存100的数组

第三个传迭代器能根据迭代器构造出一个对象

第四个是拷贝构造函数 

如果第二个中我没有指定每个元素为100,它会根据你传的模板是什么初始化为啥,例如你传int就初始化为0

3种遍历方式

1.operator[]

因为底层是数组,所以重载了operator[]方便遍历

for(size_t i=0;i<vt.size();i++)
{cout<<vt[i]<<" ";
}

2.迭代器遍历

 

 可以看到vector容器提供了begin/end和rbegin/rend,这说明我们可以正反向遍历 

vector<int>::iterator it=vt.begin();
while(it!=vt.end())
{cout<<*it;it++;
}vector<int>::reverse_iterator rit=rbegin();
while(it!=rbegin())
{cout<<*it;it++;
}

3.返回for遍历:底层就是迭代器,只要能实现迭代器就能实现范围for

for(auto&e:vt)
{cout<<e;
}

注意:如果你有一个函数,例如打印函数,你是不想修改数组里面元素的值,那传参时需要传const修饰的数组,此时你在打印函数中使用迭代器,只能用const迭代器 

迭代器分两个版本,一个是const迭代器一个是普通迭代器

void Print(const vector<int>&vt)
{vector<int>::const_iterator it=vt.begin();while(it!=vt.end())
{
cout<<*it;
*it+=1;//这句是错的,你传了const就意味着你不能通过迭代器去修改
}
}

 capacity函数接口

size():顾名思义就是返回数组的大小,这里的大小是有效元素的大小

capacity():是返回这里数组的容量,也就是你动态开辟出来的大小

empty():判断是否为空;如果没有元素即返回true,有就返回false

resize():改变数组的size

reserve():改变数组的容量,如果提前知道空间可以减少增容的代价

注意:vs2022是以1.5倍增长,gcc是以两倍增长,两个用的STL版本不一样,vs使用PJ版本,gcc是用SGI版本

Modifiers函数接口

数组当然实现的是尾插和尾删,效率高,如果你有头插和头删最好别选vector(后面会介绍list和vector的优缺点)

push_back():尾插一个数据

pop_back():尾删一个数据

insert():任意位置的插入,在pos位置之前插入

注意:这里传迭代器 

erase():任意位置的删除,删除pos位置的元素,返回下一个元素的迭代器

注意:这里我们不知道要删除的元素的位置该如何找到呢?vector没有实现find

可以调用<algorithm>库实现的find

找到返回pos位置的迭代器,找不到返回end();

 swap():交换两个vector的数据空间

clear():清空元素,但开辟的空间没有释放

 重点谈迭代器失效问题

迭代器的主要作用是让算法不用关系底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*。因此,迭代器失效,实际就是迭代器底层对应的指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果就是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)

第一种情况:

如果你先定义了迭代器vector<int>::iterator it=vt.begin();

然后在进行各种扩容操作reserve,resize,insert,assign,push_back等

然后在使用迭代器,那原来的指针it指向的的那块空间已经被释放了,因为你进行了扩容开辟了新空间,你在使用迭代器去解引用或者it!=vt.end()等操作都可能会造成程序崩溃

第二种情况:

比如你使用库实现的find去查找某个元素,会返回一个迭代器类型,然后在释放这个位置的元素

vector<int>::iterator pos=find(vt.begin(),vt.end(),3);
if(pos!=vt.end())
{erase(pos);
}
cout<<*pos<<;//此处会导致非法访问

erase删除pos位置之后,pos位置之后的元素会往前挪动,没有导致底层空间的改变,理论上讲迭代器是不会失效的,但是,如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,pos就会失效,因此删除vector中任意位置的元素是,vs就认为该位置迭代器失效了 

动态二维数组的理解:

我们如何创建一个二维数组,可以想象每个vector的元素都是一个vector<int>

vector<vector<int>> vv(5);

这里可以想象一个一维数组,有五个元素,每个元素都是一个vector<int> 

那我们如何初始化呢 

 第一种:我们学过vector<int> v(3,5)//这是创建3个元素,每个元素都是5,因此我们可以这与初始化二维数组,rows是行,每个元素是vector<int>(cols),initial)

int main() {// 定义二维数组的行数和列数int rows = 3;int cols = 4;// 初始值int initialValue = 0;// 创建并初始化二维 vectorstd::vector<std::vector<int>> twoDArray(rows, std::vector<int>(cols, initialValue));// 输出二维数组for (const auto& row : twoDArray) {for (int element : row) {std::cout << element << " ";}std::cout << std::endl;}return 0;
}

第二种:

可以创建好二维数组,在用循环依次填入数据 


int main() {// 定义二维数组的行数和列数int rows = 3;int cols = 4;// 初始值int initialValue = 0;// 创建空的二维 vectorstd::vector<std::vector<int>> twoDArray;// 逐行添加元素for (int i = 0; i < rows; ++i) {std::vector<int> row(cols, initialValue);twoDArray.push_back(row);}// 输出二维数组for (const auto& row : twoDArray) {for (int element : row) {std::cout << element << " ";}std::cout << std::endl;}return 0;
}

 第三种:使用c++11的初始化列表进行初始化

int main() {// 使用列表初始化创建二维 vectorstd::vector<std::vector<int>> twoDArray = {{1, 2, 3},{4, 5, 6},{7, 8, 9}};// 输出二维数组for (const auto& row : twoDArray) {for (int element : row) {std::cout << element << " ";}std::cout << std::endl;}return 0;
}

注意:当我们创建好了之后,可以像一个二维数组一样使用vv[i][j]去访问每个元素

实质就是operator[]重载运算符,第一个[]先和vv结合,第二个后结合就可以访问了 

 简单模拟实现vector容器、

我已经把代码上传gitee,有需要可以自己拿

 June: 这里包含我的c++和Linux及数据结构https://gitee.com/taifanshu/day2.git

相关文章:

  • Java语言概述
  • 52、【OS】【Nuttx】【OSTest】setvbuf 测试
  • mysql索引及数据库引擎
  • MySQL 第一讲---基础篇 安装
  • 第7篇:RESTful API设计与安全防护
  • 赎金信(简单)
  • 第十六届蓝桥杯 2025 C/C++组 客流量上限
  • 【CTFer成长之路】XSS的魔力
  • 【Java IO流】File类基础详解
  • 瑞萨 EZ-CUBE2 调试器
  • OSPF路由协议配置
  • 变色龙-第16届蓝桥第5次STEMA测评Scratch真题第1题
  • Flowable7.x学习笔记(十七)审批我的待办
  • stm32教程:软件I2C通信协议 代码模板提供
  • day03_ 基本数据类型丶常量丶变量
  • UE自动索敌插件Target System Component
  • Linux_su命令
  • 三类思维坐标空间与时空序位信息处理架构
  • 6.DOS
  • AI编译器对比:TVM vs MLIR vs Triton在大模型部署中的工程选择
  • AI世界的年轻人|与总书记交流过的“00后”,渴望紧握时代赋予的接力棒
  • 首次面向上海、江苏招收本科生,西湖大学接连发布招生简章
  • 对谈|“对工作说不”是不接地气吗?
  • 上海浪琴环球马术冠军赛明日启幕!五一假期在这里感受精彩
  • 网警查处编造传播“登顶泰山最高可得3万奖金”网络谣言者
  • 讲座|为什么要不断地翻译叶芝的诗?它们为什么值得细读?