[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;但是文件的内容确实以下的报错…

【数据结构】利用单链表再实现通讯录

在之前顺序表的实现中&#xff0c;我们利用了顺序表实现了通讯录&#xff0c;基于上篇文章学习了单链表&#xff0c;本篇文章将介绍如何利用单链表再实现通讯录。 1. 结构体用户数据 实现结构体的函数&#xff1a; //⽤⼾数据 typedef struct PersonInfo {char name[NAME_MA…

指定GPU跑模型

加上一个CUDA_VISIBLE_DEVICES0,2就行了&#xff0c;使用0卡和2卡跑模型&#xff0c;注意多卡有时候比单卡慢&#xff0c;4090无NVlink&#xff0c;数据似乎是通过串行的方式传输到多个gpu的&#xff0c;只不过单个gpu是并行计算&#xff0c;数据在gpu与gpu之间似乎是串行传输的…

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

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

第九站:Java黑——安全编码的坚固防线(第②篇)

4. 验证和过滤输入数据示例&#xff1a;使用Apache Commons Lang 对输入数据进行验证和过滤是防止多种安全漏洞的关键步骤&#xff0c;包括但不限于SQL注入和命令注入。Apache Commons Lang库提供了一些实用方法来帮助进行字符串操作和验证。以下是一个简单的示例&#xff0c;…

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

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

掌握Mock.js基本使用:快速模拟API实现增删改查

在项目中使用Mock.js进行增删改查操作&#xff0c;可以模拟后端接口&#xff0c;方便前端开发和测试。下面是一个简单的示例&#xff0c;展示如何在Vue 2项目中使用Mock.js实现增删改查功能。 1. 安装依赖 npm install mockjs --save2. 配置Mock.js 在项目的根目录下创建一个…

代码随想录算法训练营DAY36补| 435. 无重叠区间、 763.划分字母区间

训练营计划更新了&#xff0c;补一下DAY36多出来的两道题 435. 无重叠区间 题目链接&#xff1a;435. 无重叠区间 class Solution(object):def eraseOverlapIntervals(self, intervals):""":type intervals: List[List[int]]:rtype: int"""if…

前端跨域问题的解决办法,两种类型

跨域问题是由于同源策略的原因&#xff0c;在协议(HTTP或HTTPS)不同、端口号(8080或8881)不同、主机IP(182.92.178.25或者182.92.178.26)不同时而导致的不能进行数据交换的问题。 当后端没有做跨域时&#xff0c;前端就需要自己配置。 类型一&#xff1a;使用代理服务器的方法…

《书生·浦语大模型实战营》第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…

常见的中间件都在解决什么问题?

常见的中间件都在解决什么问题 RocketMQ RocketMQ 是一款功能强大的分布式消息系统。 RocketMQ 源码地址&#xff1a;https://github.com/apache/rocketmq(opens new window) RocketMQ 官方网站&#xff1a;https://rocketmq.apache.org 什么场景下用 RocketMQ&#xff1f…

线程安全的解决方案(概念版本)

线程安全其实本质 讲的是内存的安全。 多个线程对统一资源的操作可能导致这个资源的数据不一致性。 举例说明&#xff1a;比如 张三有100块钱&#xff0c;现在线程A是要花50元&#xff0c;线程二是要存300元&#xff0c;当线程A花50元后&#xff0c;还没有内存值修改&#xff0…

19.channel相关方法

channel close()可以用来关闭channel closeFuture()用来处理channel的关闭 sync方法作用是同步等待channel关闭 addListener方法是异步等待channel关闭 pipeline() 方法添加处理器 write() 方法将数据写入 writeAndFlush() 方法将数据写入并刷出 write()方法,将数据写入到…

Optional详解和常用API

目录 一、Optional简介 二、构建Optional对象三种方式 2.1 Optional.of(value) 2.1.1 使用案例 2.2 Optional.ofNullable(value) 2.2.1 使用案例 2.3 Optional.empty() 2.3.1 使用案例 三、Optional常用的api解析和使用案例 3.1 isPresent 3.1.1 使用案例 3.2 ifPrese…

登录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. 回调函数是什么? 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数…

VMware各版本镜像下载站

CDS Repository - /var/www/public/stage/session-120/cds/vmw-desktop/ws 参考&#xff1a; VMware各版本下载的镜像站(含windows和linux)_vm win7映像文件下载-CSDN博客