[C++ STL] vector 详解

标题:[C++ STL] vector 详解

@水墨不写bug


目录

一、背景

 二、vector简介

 三、vector的接口介绍

(1)默认成员函数接口

i,构造函数(constructor)

ii,析构函数(destructor)

iii,拷贝构造

iv,赋值重载

(2)迭代器接口

(3)容量接口

i,size()

ii,max_size()

iii,resize()

iv,capacity()

v,empty()

vi,reserve()

vii,shrink_to_fit()

(4)元素访问接口

i,operator[]

ii,at()

iii,front()

iv,back()

(5)修改接口

i,push_back()

ii,pop_back()

iii,insert()

iv,erase()

v,swap()

vi,clear()



正文开始:

一、背景

        我们在学习C语言时就渴望过一种可以自动扩容的数组。尽管C99标准引入了变长数组:

变长数组

        变长数组是C99标准新增的特性,它允许在运行时动态地指定数组的长度。与传统的静态数组不同,变长数组的长度可以在程序运行时确定,而不是在编译时确定。

但是变长数组不是真正的可以随意扩容的容器:因为在运行时,变长数组的长度只能确定一次。一旦变长数组的长度确定,就无法再次改变了!

        这也是C语言的痛点,为了实现随意扩容的功能,我们可以实现一个动态顺序表(Dynamic Sequence List):

        可变长的顺序表(Dynamic Sequence List)

        是一种能够动态调整存储空间大小的顺序表。它的大小是可以根据需要进行动态扩展或缩小的,不需要在初始化时指定固定的容量。

        实现可变长顺序表的方式有多种,其中一种常见的方式是使用动态数组。动态数组是在内存中分配一块连续的存储空间来存储元素,当元素个数超过当前分配的空间大小时,会重新分配更大的空间,并将原有的元素复制到新的空间中,然后释放原有的空间。

但是每次在使用前,都要实现一个顺序表太麻烦了,用C语言实现一个顺序表动辄就几百行,这样不断重复“造轮子”的行为导致开发十分低效。在这样的背景下,C++中引入了STL:

         STL(Standard Template Library)

        是C++标准库中的一个重要组成部分,提供了一系列的模板类和算法,用于处理常见的数据结构和算法问题。STL包括了容器(Containers)、迭代器(Iterators)、算法(Algorithms)和函数对象(Function Objects)等组件,提供了丰富的数据结构和算法操作。

 其中的容器,就是帮助我们解决问题的组件。本文讲解的vector就是容器中的非常常用常见的一个。


 二、vector简介

        vector是STL中的一个容器,实现了动态数组的功能。它可以存储任意类型的元素,并提供了一系列的操作函数,如插入、删除、访问等。

vector的特点包括:

  1. 动态大小:vector的大小可以根据需要进行动态调整,可以随时增加或减少元素的数量。
  2. 连续存储:vector的元素在内存中是连续存储的,可以通过下标快速访问元素。
  3. 头部插入删除效率较低:由于在头部插入或删除元素时需要进行大块内存的复制或移动,导致效率较低。
  4. 随机访问效率高:由于元素的连续存储,可以通过下标直接访问任意位置的元素。

 三、vector的接口介绍

        由于在实际应用中,vector的一些接口并不常用,所以本介绍尽量在完整的基础上简洁。

(1)默认成员函数接口

         在正式开始介绍之前,我们先认识一个之前已经见过的关键字:

explicit:

        在C++中,explicit 是一个关键字,用于防止类构造函数进行隐式类型转换。当你有一个类,并且这个类有一个可以从其他类型隐式转换的构造函数时,你可能会在无意中创建该类的对象,而不是你原本想要的。使用 explicit 关键字可以确保这种转换是显式的,从而避免潜在的错误。

class MyClass {  
public:  explicit MyClass(int value) {  // ...  }  
};  void foo(MyClass obj) {  // ...  
}  int main() {  // 下面的代码会因为 MyClass 的构造函数是 explicit 的而编译失败  // foo(42); // 错误:无法从 'int' 转换为 'MyClass'  // 你必须显式地创建一个 MyClass 对象  foo(MyClass(42)); // 正确  return 0;  
}

        这样就可以开始介绍了。

i,构造函数(constructor)

        

default (1)
explicit vector (const allocator_type& alloc = allocator_type());

默认构造,创建一个没有数据的vector。

vector<int> v;
fill (2)
explicit vector (size_type n);vector (size_type n, const value_type& val,const allocator_type& alloc = allocator_type());

创建一个有n个元素的vector,每个元素初始化为提供的val的值。(如果没有提供val,则默认初始化为0)

	vector<int> v(10);cout<<v[6]<<endl;//输出0

range (3)
template <class InputIterator>vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());

迭代器初始化,根据提供的容器迭代器范围内的数据来初始化vector。

list<int> l = { 1,2,5,73 };
vector<int> v(l.begin(), l.end());
//v的数据也是1,2,5,73

ii,析构函数(destructor)

~vector();

销毁容器对象。(编译器会自动调用)

iii,拷贝构造

copy 
vector (const vector& x);
vector (const vector& x, const allocator_type& alloc);

用x中每个元素的的值按相同顺序构造一个vector。

vector<int> v1 = { 1,8,6,99 };//后面这两行的两种写法是等价的
vector<int> v2 = v1;
vector<int> v2(v1);

iv,赋值重载

copy (1)
vector& operator= (const vector& x);

对“=”操作符重载。

vector<int> v = { 8,5,6 };
vector<int> v1 = {5,8,99};//完成自定义对象之间的值拷贝
v1 = v;


noexcept: 

    noexcept 是 C++11 引入的一个关键字,用于指定某个函数或函数模板是否抛出异常。如果一个函数被声明为 noexcept,那么它承诺在执行过程中不会抛出任何类型的异常。

(2)迭代器接口

iterator begin() noexcept;
const_iterator begin() const noexcept;

返回容器指向第一个元素的迭代器,普通对象调用普通的,const对象调用const的函数。

(由于次类型函数仅仅是返回一个迭代器类型的变量,不会产生抛异常的现象;同时一个函数被声明为 noexcept,那么编译器可能会生成更高效的代码,因为它不需要为这个函数准备异常处理逻辑。所以为了提高效率,加上了noexcept

 

iterator end() noexcept;
const_iterator end() const noexcept;

返回容器指向最后一个元素的下一个位置的迭代器,普通对象调用普通的,const对象调用const的函数。

 

 reverse_iterator rbegin() noexcept;
const_reverse_iterator rbegin() const noexcept;

返回容器指向最后一个元素的迭代器,普通对象调用普通的,const对象调用const的函数。

 

 reverse_iterator rend() noexcept;
const_reverse_iterator rend() const noexcept;

返回容器指向第一个元素的前一个位置的迭代器,普通对象调用普通的,const对象调用const的函数。

 

const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;

这些是STL中提供的上面迭代器的const版本。但其实begin已经有重载一个const版本了,所以const迭代器也可以接受begin(const重载)的返回值。于是这4个接口即使不存在,也不会影响vector的const迭代器的正常使用。

(3)容量接口

i,size()

size_type size() const noexcept;

返回vector中元素的个数。这是vector中保存的实际对象的数量,不一定等于它的存储容量。

vector<int> v={1,2,3};
vector<int>::const_iterator it = v.cbegin();
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
v.push_back(8);
v.push_back(9);
v.push_back(10);
v.push_back(11);
v.push_back(12);
v.push_back(13);
v.push_back(14);
cout << v.size() << ":" << v.capacity()<<endl;

ii,max_size()

size_type max_size() const noexcept;

返回vector所能容纳的最大元素数。由于已知的系统或库实现限制,这是容器可以达到的最大理论大小,但是容器不能保证能够达到这个大小。所以max_size()没有实际的参考意义。

iii,resize()

void resize (size_type n);
void resize (size_type n, const value_type& val);

传入一个参数n,n是指定的size大小:

        如果n小于size,则缩容到n;

        如果n等于size,不操作;

        如果n大于size:

                如果n小于等于capacity,不扩容,将size增大到n,并用指定的val初始化新扩容的元素。(如果没有指定,默认用0初始化)

                如果n大于capacity,需要扩容,扩容的规模 根据编译器的处理而定 ,扩容之后将size增大到n,并用指定的val初始化新扩容的元素。(如果没有指定,默认用0初始化)

iv,capacity()

size_type capacity() const noexcept;

返回当前为vector分配的存储空间大小,以元素表示。

v,empty()

bool empty() const noexcept;

返回向量是否为空(即它的大小是否为0)。

为空返回真,非空返回假。

vi,reserve()

void reserve (size_type n);

要求vector容器容量至少足以容纳n个元素。

        如果n大于当前的vector容量,则该函数使容器重新分配其存储空间,将其容量增加到n(或更大)。

        在所有其他情况下,函数调用不会导致重新分配,向量容量也不会受到影响。这个函数对vector的size没有影响,也不能改变vector的元素。

vii,shrink_to_fit()

void shrink_to_fit();

请求容器减少其容量以适合其大小。


(4)元素访问接口

i,operator[]

   reference operator[] (size_type n);
const_reference operator[] (size_type n) const;

返回对vector容器中位置n的元素的引用。普通对象调用普通函数,const对象调用调用const函数,并且const函数的函数返回的引用不能被修改。

        类似的成员函数vector::at()具有与此操作符函数相同的行为,除了vector::at是绑定检查的,并且如果请求的位置超出范围,则通过抛出out_of_range异常来发出信号。

        可移植强的程序 不应该使用超出范围的参数n调用此函数,因为这会导致未定义的行为。

ii,at()

reference at (size_type n);
const_reference at (size_type n) const;

返回对vector中位置n的元素的引用。

该函数自动检查n是否在vector中有效元素的范围内,如果不在,则抛出out_of_range异常(即,如果n大于或等于其大小)。这与成员operator[]相反,operator[]不检查边界。

iii,front()

reference front();
const_reference front() const;

返回对vector中第一个元素的引用。

        普通对象调用普通函数,const对象调用调用const函数,并且const函数的函数返回的引用不能被修改。

        与返回指向同一元素的迭代器的成员vector::begin不同,此函数返回直接引用。在空容器上调用此函数会导致未定义行为。

iv,back()

 reference back();
const_reference back() const;

返回对vector中最后一个元素的引用。

        普通对象调用普通函数,const对象调用调用const函数,并且const函数的函数返回的引用不能被修改。

        与成员vector::end不同,后者返回经过该元素的迭代器,此函数返回直接引用。在空容器上调用此函数会导致未定义行为。

(5)修改接口

i,push_back()

void push_back (const value_type& val);
void push_back (value_type&& val);

在vector当前最后一个元素的末尾添加一个新元素。val的内容被复制(或移动)到新元素中。

        这将容器大小增加了1,当且仅当新向量大小超过当前向量容量时,会自动重新分配已分配的存储空间。

ii,pop_back()

void pop_back();

移除vector中的最后一个元素,有效地将容器大小减小1。

iii,insert()

single element (1)
iterator insert (const_iterator position, const value_type& val);

在下标为position位置的迭代器插入一个元素val。

vector<int> v = { 1,2,3 };
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
v.insert(v.begin(), 666);

fill (2)
iterator insert (const_iterator position, size_type n, const value_type& val);

在下标为position位置的迭代器插入n个元素val。

vector<int> v = { 1,2,3 };
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
v.insert(v.begin(),3, 666);

range (3)
template <class InputIterator>
iterator insert (const_iterator position, InputIterator first, InputIterator last);

迭代器区间初始化,在下标为position位置的迭代器插入一个迭代器区间内的值。

vector<int> v = { 1,2,3 };
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);vector<int> v1 = { -999,-999 };
list<int> l1 = { 333,333 };
v.insert(v.begin(), v1.begin(), v1.end());
v.insert(v.end(), l1.begin(), l1.end());

​​​​​​​

iv,erase()

iterator erase (const_iterator position);
iterator erase (const_iterator first, const_iterator last);

        从vector对象中移除单个元素(position)或一段元素([first,last))。这有效地减少了容器的大小,通过删除元素的数量,这些元素被销毁。

        由于vector使用数组作为其底层存储,因此删除位于vector末端以外位置的元素会导致容器将擦除段后的所有元素重新移动到新位置。

        与其他类型的序列容器(如list或forward_list)所执行的相同操作相比,这通常是一个效率低下的操作。

v,swap()

void swap (vector& x);

        用x的内容交换容器的内容,x是另一个相同类型的vector对象。大小可能不同。在调用this成员函数之后,this容器中的元素是调用之前在x中的元素,而x的元素是在this容器中的元素。

        所有迭代器、引用和指针对于交换后的对象仍然有效。注意,存在一个具有相同名称的非成员函数,swap,用行为类似于该成员函数的优化重载该算法。

vi,clear()

void clear() noexcept;

从vector中移除所有元素(这些元素被销毁),使容器的大小为0。


完~

未经作者同意禁止转载 

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

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

相关文章

Verilog综合出来的图

Verilog写代码时需要清楚自己综合出来的是组合逻辑、锁存器还是寄存器。 甚至&#xff0c;有时写的代码有误&#xff0c;vivado不能识别出来&#xff0c;这时打开综合后的schematic简单查看一下是否综合出想要的结果。 比如&#xff1a;误将一个always模块重复一遍&#xff0c;…

天翼云认证专家解决方案架构师(理论)

1.某大型互联网公司为了提升应用程序和基础设施的稳定性&#xff0c;计划引入自动化监控工具。以下哪些工具可以满足公司的需求? A.Grafana B.Nagios C.Prometheus D.Jenkins 2.天翼智能边缘云ECX是位于网络边缘位置的云&#xff0c;兼具云和CDN的特性&#xff0c;将计算、存…

使用百度的长文本转语音API时无法下载.MP3文件

今天是学生们交作业的时候&#xff0c;结果是我最忙碌的一天&#xff0c;各种改bug。 有个学生来问&#xff1a; 我在百度提供的API代码(长文本转语音)的基础上添加了下载生成的.MP3文件的代码&#xff0c;运行之后成功建成了.MP3文件&#xff0c;但是文件的内容确实以下的报错…

如何通过Outlook大附件插件,加强外发附件的安全性和管控力度?

因邮件的便捷性和普遍性&#xff0c;企业间业务往来通常会采取邮箱业务&#xff0c;沟通使用成本也比较低&#xff0c;但容易出现附件太大无法上传的问题。Outlook大附件插件是为解决邮件系统中附件大小限制问题而开发的一系列工具。 使用邮件发送附件时&#xff0c;可能会遇到…

基于51单片机万年历设计—显示温度农历

基于51单片机万年历设计 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 本系统采用单片机DS1302时钟芯片LCD1602液晶18b20温度传感器按键蜂鸣器设计而成。 1.可以显示年月日、时分秒、星期、温度值。…

《书生·浦语大模型实战营》第4课 学习笔记:XTuner 微调 LLM:1.8B、多模态、Agent

文章大纲 1. 大模型微调简介2 快速上手2.1 环境安装2.2 前期准备2.2.1 数据集准备2.2.2 模型准备2.2.3 配置文件选择2.2.4 小结 2.3 配置文件修改2.4 模型训练2.4.1 常规训练2.4.2 使用 deepspeed 来加速训练2.4.3 训练结果2.4.4 小结 2.5 模型转换、整合、测试及部署2.5.1 模型…

【QT5】<重点> QT串口编程

目录 前言 一、串口编程步骤 0. 添加串口模块 1. 自动搜索已连接的串口 2. 创建串口对象 3. 初始化串口 4. 打开串口 5. 关闭串口 6. 发送数据 7. 接收数据 二、简易串口助手 1. 实现效果 2. 程序源码 3. 实现效果二 前言 本篇记录QT串口编程相关内容&#xff0…

登录MySQL方式

登录MySQL方式 方式一&#xff1a;通过MySQL自带的客户端 MySQL 客户端输入命令即可 方式二&#xff1a;通过window自带的客户端 从命令端&#xff08;cmd&#xff09;进入 mysql -h localhost -P 3306 -u root -p Enter password:密码登录方式&#xff1a; mysql -h 主…

深入理解指针(四)

目录 1. 回调函数是什么? ​2. qsort使用举例 2.1冒泡排序 2.2使用qsort函数排序整型数据 ​2.3 使用qsort排序结构数据(名字) 2.4 使用qsort排序结构数据(年龄) 3. qsort函数的模拟实现 1. 回调函数是什么? 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数…

混淆矩阵-召回率、精确率、准确率

混淆矩阵 1 混淆矩阵2 混淆矩阵指标2.1 准确率2.2 精确率2.3 召回率2.4 特异度2.4 假正率2.5 假负率2.6 F1 分数 3 总结 1 混淆矩阵 混淆矩阵是一种用于评估分类模型性能的重要工具。它通过矩阵形式清晰地展示了模型对样本进行分类的结果&#xff0c;帮助我们理解模型在不同类…

【嵌入式】CAN总线详解

【嵌入式】CAN总线详解 一、CAN总线简介 CAN总线是一种控制器局域网总线&#xff0c;每一个挂载在CAN局域网的设备&#xff0c;都可以利用CAN去发送信息&#xff0c;也可以接收局域网的各种信息&#xff0c;每个设备都是平等的&#xff0c;共享CAN的资源。广泛应用于汽车、嵌…

LabVIEW程序的常见加密方式

LabVIEW程序的加密对于保护知识产权和敏感数据至关重要。本文将详细介绍LabVIEW程序常用的加密方式&#xff0c;包括VI加密、代码保护、文件加密和通信加密等&#xff0c;帮助开发者选择合适的加密方法来确保程序的安全性和完整性。 LabVIEW程序的常见加密方式 VI加密&#xf…

【宠粉赠书】科研绘图神器:MATLAB科技绘图与数据分析

小智送书第二期~ 为了回馈粉丝们的厚爱&#xff0c;今天小智给大家送上一套科研绘图的必备书籍——MATLAB科技绘图与数据分析。下面我会详细给大家介绍这套图书&#xff0c;文末留有领取方式。 图书介绍 《MATLAB科技绘图与数据分析》是一本综合性强、内容丰富的书籍&#x…

Mybatis做批量操作

动态标签foreach&#xff0c;做过批量操作&#xff0c;但是foreach只能处理记录数不多的批量操作&#xff0c;数据量大了后&#xff0c;先不说效率&#xff0c;能不能成功操作都是问题&#xff0c;所以这里讲一讲Mybatis正确的批量操作方法&#xff1a; 在获取opensession对象…

哈喽GPT-4o——对GPT-4o 提示词的思考与看法

目录 一、提示词二、常用的提示词案例1、写作助理2、改写为小红书风格3、英语翻译和改写4、论文式回答5、主题解构6、提问助手7、Nature风格润色8、结构总结9、编程助手10、充当终端/解释器 大家好&#xff0c;我是哪吒。 最近&#xff0c;ChatGPT在网络上广受欢迎&#xff0c…

PDFFactoryFinePrint软件安装包下载+详细安装教程

简介&#xff1a; pdfFactory Pro(虚拟打印机)是一个无须 Acrobat 创建 Adobe PDF 文件的打印机驱动程序。 pdffactory pro虚拟打印机提供了比其他程序提供得更简单、更有效率和更少的花费的创建 PDF 文件的解决方案。用于需要安全的 PDF(法律文档、公司信息等)和其他高级功能…

js实现一个数据结构——栈

栈的概念就不再赘述&#xff0c;无可厚非的先进后出&#xff0c;而JS又是高级语言&#xff0c;数组中的方法十分丰富&#xff0c;已经自带了push pop方法进行入栈出栈的操作。 1.基本实现 class Stack {constructor() {this.items [];}// 入栈push(item) {this.items.push(i…

List 列表

文章目录 一、什么是 List 列表1.1 创建 List 列表的方式1.2 列表的新增函数方法1.3 列表的删除函数方法1.4 修改列表数据的方法1.5 列表的查询函数方法1.6 列表的排序和反序1.7 列表的复制 一、什么是 List 列表 List 列表&#xff1a;该数据类型定义的变量可以理解为是一个数…

【Java】已解决java.lang.NoClassDefFoundError异常

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.lang.NoClassDefFoundError异常 一、问题背景 java.lang.NoClassDefFoundError 是 Java 运行时环境&#xff08;JRE&#xff09;在尝试加载某个类时&#xff0c;但没有找到…

基于51单片机贪吃蛇小游戏

基于51单片机贪吃蛇小游戏 &#xff08;仿真&#xff0b;程序&#xff09; 功能介绍 具体功能&#xff1a; 1.用74HC573驱动点阵显示游戏画面&#xff1b; 2.上电后贪吃蛇会自动寻食&#xff1b; 3.按下四个按键中的任何一个就手动寻食了&#xff1b; ​演示视频&#xf…