【C++】vector的介绍及使用说明(类模版的实现方式,顺序存储与动态数组,迭代器iterator的运用,vector的增删查改)

目录

 

00.引言

01.vector的介绍

类模版

动态分配内存

顺序存储

02.vector的使用

构造函数

迭代器iterator

1.分类:

2.运用:

扩容

1.resize()

2.reverse()

增删查改

1.增加

2.删除

3.查找

4.修改


 

00.引言

        以前我们讲过string类,std::string类负责字符串的存储和操作,它在内部使用动态内存分配来存储字符串,并且会自动扩展和释放内存空间,以适应字符串的长度变化,这种自动分配内存空间使得其非常方便和易用,而且它也提供了非常多的方法来对字符串进行操作,为我们节省了非常多的时间。博客链接:string类的底层实现,string类的介绍与使用

        但美中不足的是string类专门用于处理字符串,对于非字符串则无法使用string类,我们也知道模版的概念,一个模版函数可以支持不同类型的参数,一个模版类同样也可以根据不同的数据类型生成不同的类实例,vector就是么一个模版类。哦不,准确的来说,vector是一个用模版类实现的动态数组容器,容器的概念有别于类,后面会写一篇博客来专门讲解。

01.vector的介绍

类模版

"std::vector"实现不同类型数据的管理是通过类模版的机制实现的。当创建一个vector对象时,可以通过模版参数来指定存储的元素类型。使用"<数据类型>"来显式实例化指定存储的元素类型,例如“std::vector<int>”表示存储整数类型的向量,“std::vector<double>”表示存储双精度浮点型的向量……

#include <iostream>

template<typename T>
class vector {
    ……
};

int main() {
    vector<int> myVector1;
    vector<double> myvector2;

    return 0;
}

动态分配内存

vector实现动态内存分配主要思路用指针跟踪数组元素,并记录当前容量,需要时在堆上重新申请空间进行扩容。

初始分配:初始时,vector分配一块初始大小的内存空间用于存储元素,初始分配的大小通常比较小,甚至可能是0

插入元素:当向vector中插入新元素时,如果当前元素数量小于容量,则直接将新元素放入数组中。如果当前元素数量已经达到了容量,就需要进行内存扩容。

内存扩容:vector会申请一块新的内存空间,通常是原容量的两倍。然后将原有元素复制到新的内存空间之中,并释放原内存空间。这个过程称为“重新分配”或“扩容”。

更新容量和指针:内存扩容完成后,vector会更新容量信息和指向动态数组的指针,以反映新的内存分配情况。

缩小容量:当vector中的元素数量减少到一定程度时,vector容器可能会选择减少其内存空间,以使得容量与元素想适应。

6e54db49c63847f59316ca9fc2aa2fba.png

优缺点分析:

优点:1.动态分配内存大小,提高了代码的灵活性和通用性 2.根据实际的数据量进行相应的内存分配,提高了内存的使用效率,节省了内存空间。

缺点:1.由于扩容需要重新开辟内存空间,这会带来一定的性能开销 2.如果内存分配或释放不当,会导致内存泄漏的问题。

顺序存储

vector内部元素在内存中时连续存储的,因此vector容器具有数据结构顺序表的特点

1.可以以O(1)的时间复杂度对vector中的元素进行随机访问.

2.在尾部插入和删除元素的时间复杂度是O(1).

3.在中间位置插入和删除元素的时间复杂度是O(n),因为需要挪动其他元素.

02.vector的使用

在cplusplus网站里有具体的vector使用文档:vector的使用文档,在实际应用中我们需要熟悉一些常见的vector接口,以下列出了一些常见的接口。

构造函数

在使用vector创建对象之前,需要包含头文件“<vector>”

我们需要调用构造函数来创建对象,vector的构造函数有以下几种:

1.vector(); 无参构造

2.vector(size_type n, const value_type& val = value_type()); 初始化n个val

3.vector(const vector& x); 拷贝构造

4.vector(InputIterator first, InputIterator last); 迭代器初始化构造

代码演示:

#include <iostream>
using namespace std;
#include <vector>

//vector的构造
int TestVector1()
{
    vector<int> first;                                // 无参构造
    vector<int> second(4, 100);                       // 初始化4个值为100的元素
    vector<int> third(second.begin(), second.end());  // 迭代器初始化
    vector<int> fourth(third);                        // 拷贝构造
    return 0;
}

迭代器iterator

1.分类:

1.begin + end:获取第一个数据位置的iterator,获取最后一个数据的下一个位置的iterator

2.rbegin + rend:获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的 reverse_iterator

这里为什么end和rend分别获取的是下一个位置和前一个位置而不是元素本身呢?考虑到以下原因

1.在使用迭代器遍历容器时,通常是通过比较该迭代器与end迭代器来确定是否到达末尾,如果end()返回的是最后一个元素本身,那么有可能容器中存在多个与末尾元素相同的元素,此时就不好进行判断;而end()返回的是nullptr就很好进行判断了。

2.可以确保在空容器中begin和end返回的是同一位置。

2.运用:

迭代器类似于指针,可以使用‘*’操作符对迭代器进行解引用,使用‘++’操作符对迭代器进行前进

因此迭代器初始化构造还可以通过以下方式:

#include <iostream>
using namespace std;
#include <vector>

int TestVector1()
{
    int myints[] = { 16,2,77,29 };
    vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int));

    cout << "容器5的内容是:";
    for (auto it = fifth.begin(); it != fifth.end(); ++it)
        cout << ' ' << *it;
    cout << '\n';

    return 0;
}

int main()
{
    TestVector1();
    return 0;
}

迭代器遍历打印

请看以下代码实例:

void PrintVector(const vector<int>& v)
{
	// const对象使用const迭代器进行遍历打印
	vector<int>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

void TestVector2()
{
	// 使用push_back插入4个数据
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	// 使用迭代器进行遍历打印
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	// 使用迭代器进行修改
	it = v.begin();
	while (it != v.end())
	{
		*it *= 2;
		++it;
	}

	// 使用反向迭代器进行遍历再打印
	// vector<int>::reverse_iterator rit = v.rbegin();
	auto rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;

	PrintVector(v);
}

扩容

1.resize()

resize()函数用于调整容器大小,它可以增加或减少容器的元素数量。其具有以下特点:

  • 如果新的大小大于当前大小,则向向量中添加新的元素,并使用默认值初始化这些新元素。

  • 如果新的大小小于当前大小,则丢弃超出新大小范围的元素。

  • resize() 操作可能导致向量的内存重新分配,因为它可能会增加或减少内存的使用量。

  • 通常在需要动态调整向量大小的情况下使用,比如在读取数据时动态添加元素或者在数据处理过程中动态删除元素。

示例:

void TestVector3()
{
	vector<int> v;

	//向容器内插入元素:
	for (int i = 1; i < 10; i++)
		v.push_back(i);

	v.resize(5);
	v.resize(8, 100);
	v.resize(12);

	cout << "v contains:";
	for (size_t i = 0; i < v.size(); i++)
		cout << ' ' << v[i];
	cout << '\n';
}

2.reverse()

reverse()函数用于预留向量的存储空间,但并不改变向量大小。其有以下特点:

  • reserve() 函数预留了足够的内存空间,以容纳指定数量的元素,但并不会改变向量的大小。也就是说,向量的 size() 不会改变,但是 capacity() 可能会增加。

  • reserve() 操作不会改变向量中元素的数量或值,只是为了避免后续的内存重新分配操作,从而提高性能。

  • 通常在已知需要存储大量元素时,可以先使用 reserve() 预留足够的内存空间,以避免插入元素时的频繁内存重新分配。

示例:

void TestVectorExpandOP()
{
	vector<int> v;
	size_t sz = v.capacity();
	v.reserve(100);   // 提前将容量设置好,可以避免一遍插入一遍扩容
	cout << "making bar grow:\n";
	for (int i = 0; i < 100; ++i) 
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

resize()reserve() 的区别在于:resize() 用于调整向量的大小并添加或删除元素,而 reserve() 用于预留向量的内存空间而不改变向量的大小。

增删查改

1.增加

pushback()方法向容器的末尾添加新的元素。

std::vector<int> vec;
vec.push_back(10); // 在向量末尾添加元素 10

insert()方法在指定位置插入新的元素。

std::vector<int> vec = {1, 2, 3, 4};
vec.insert(vec.begin() + 2, 5); // 在位置 2 插入元素 5

2.删除

pop_back()方法删除末尾元素。

std::vector<int> vec = {1, 2, 3, 4};
vec.pop_back(); // 删除末尾元素

erase()方法删除指定位置或范围的元素。

std::vector<int> vec = {1, 2, 3, 4};
vec.erase(vec.begin() + 1); // 删除位置 1 的元素
vec.erase(vec.begin() + 1, vec.begin() + 3); // 删除位置 1 到 2 的元素

3.查找

下标操作符 [] 或者 at() 方法访问指定位置的元素。

std::vector<int> vec = {1, 2, 3, 4};
int value = vec[2]; // 获取位置 2 的元素值

迭代器进行遍历和访问向量中的元素。

for (auto it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " "; // 输出向量中的元素
}

4.修改

下标操作符 [] 或者 at() 方法修改指定位置的元素的值。

std::vector<int> vec = {1, 2, 3, 4};
vec[2] = 5; // 将位置 2 的元素值修改为 5

迭代器进行遍历和修改向量中的元素。

for (auto it = vec.begin(); it != vec.end(); ++it) {
    *it *= 2; // 将向量中的元素值乘以 2
}

以上就是关于vector的介绍和使用说明的有关知识了,欢迎在评论区留言~感觉这篇博客对您有帮助的可以点赞关注支持一波喔~😉

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何系统的自学python?

系统地自学Python是一个循序渐进的过程&#xff0c;以下是一份详细的指南&#xff0c;帮助你从零开始逐步掌握这门语言&#xff1a; 1、了解Python及其应用场景&#xff1a; 阅读关于Python的简介&#xff0c;理解它为何流行&#xff0c;以及在哪些领域&#xff08;如Web开发…

stream流中的坑,peek/map/filter

起因 所在系统为一个对账系统&#xff0c;涉及的业务为发布账单&#xff0c;数据结构定的是供应商账单发布&#xff0c;生成企业账单和个人账单。发布账单处理完本系统业务后&#xff0c;需要生成站内通知和调用外部接口生成短信通知。后来增加需求&#xff0c;需要在发布完成…

【Qt 学习笔记】Day1 | Qt 开发环境的搭建

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Day1 | Qt 开发环境的搭建 文章编号&#xff1a;Qt 学习笔记 / 02 文…

C++初学者:优雅创建第一个窗口

我想学习C做一些实用的程序&#xff0c;但是我不想在软件界面上花太多的时间&#xff0c;可是每每就是界面影响我的思绪。 今天学习C类的包装知识&#xff0c;终于整出了一个我的界面类&#xff0c;虽然封装水平很弱&#xff0c; 这次就用这个类&#xff0c;写了自己工作上常用…

Node.js中Router的使用

文章目录 介绍router的优点1.导入Express和创建Router&#xff1a;2. 定义路由&#xff1a;3.将router暴露到模块外&#xff1a;4. 将Router挂载到Express应用中&#xff1a;4.1.引入router4.2.使用中间件让router在Express应用中生效(三种写法) 5. 完整示例&#xff1a;5.1.编…

Vue3+Vite Nginx部署 跨域

打包项目 webstorm打开项目之后&#xff0c;在Terminal执行打包命令 pnpm run build:prod 复制到Nginx 打包完成之后,生成的包在根目录dist&#xff0c;把dist目录拷贝到Nginx放网站目录下&#xff1a;\nginx-1.25.2\html\divided &#xff0c;dist改名了divided 修改配置…

【JavaSE】内部类

目录 前言 内部类 内部类的种类 1. 实例内部类 2 静态内部类 3 匿名内部类 4 局部内部类 结语 前言 内部类是我们前面学习遗留下来的知识点&#xff0c;在学完接口后才能更好的理解它&#xff0c;因此等到现在才讲 内部类 在Java中&#xff0c;我们可以将A类定义在B…

短视频素材哪里去找?五大网站助你轻松解决素材难题!

你好&#xff0c;短视频小能手们&#xff0c;是不是经常在为找不到好看的视频素材而烦恼&#xff1f;不用怕&#xff0c;今天我要为你们揭秘五个超赞的视频素材网站&#xff0c;让你的视频素材&#xff0c;制作事半功倍&#xff0c;轻松赢得点赞和关注&#xff01;瞬间成为热门…

关于Windows中AppData的相关知识,看这篇文章就可以了

如果AppData文件夹占用了你电脑上的太多空间,则需要清理AppData文件夹。下面是一些帮助你在Windows计算机上进行AppData清理的方法。 什么是AppData文件夹 AppData文件夹是保存应用程序数据和设置的位置。每个Windows计算机在C驱动器上都有一个AppData文件夹。AppData文件夹…

自己动手用ESP32手搓一个智能机器人:ESP32-CAM AI Robot

目录 介绍 硬件需求 软件需求 步骤 总结 源码下载 介绍 ESP32-CAM是一款集成了Wi-Fi和蓝牙功能的微控制器模块&#xff0c;同时还集成了摄像头接口&#xff0c;使其成为一个非常适合构建智能机器人的选择。在本项目中&#xff0c;我将向您展示如何使用ESP32-CAM模块构建…

C# winform校验文件版本差异及版本号

界面 代码 using System.Diagnostics;namespace VersionTool {public partial class Form1 : Form{List<string> fileNmaes new List<string>() { "PhotoMes.Base.dll", "PhotoMes.App.exe", "PhotoMes.Cameras.dll" };public F…

JavaScript高级 —— 学习(二)

一、深入对象 &#xff08;一&#xff09;创建对象三种方式 1.利用对象字面量创建 <body><script>const obj {}</script> </body> 2.利用 new Object() 创建 <body><script>const obj new Object({uname: 一个人})console.log(obj)…

AcWing刷题(每日一题)-区间合并

挤牛奶 区间合并&#xff1a; &#xff08;写的有丢丢乱T_T&#xff09; from typing import List def merge(intervals: List[List[int]]) -> List[List[int]]:# 按照第一个元素从小到大进行排序intervals.sort(keylambda x: x[0])# 初始化一个新的数组new_list list()f…

《操作系统导论》第14章读书笔记:插叙:内存操作API

《操作系统导论》第14章读书笔记&#xff1a;插叙&#xff1a;内存操作API —— 杭州 2024-03-30 夜 文章目录 《操作系统导论》第14章读书笔记&#xff1a;插叙&#xff1a;内存操作API1.内存类型1.1.栈内存&#xff1a;它的申请和释放操作是编译器来隐式管理的&#xff0c;所…

Yolo 自制数据集dect训练改进

上一文请看 Yolo自制detect训练-CSDN博客 简介 如下图&#xff1a; 首先看一下每个图的含义 loss loss分为cls_loss, box_loss, obj_loss三部分。 cls_loss用于监督类别分类&#xff0c;计算锚框与对应的标定分类是否正确。 box_loss用于监督检测框的回归&#xff0c;预测框…

鸿蒙OS开发实战:【打造自己的搜索入口】

背景 几乎每家应用中都带有搜索功能&#xff0c;关于这个功能的页面不是特别复杂&#xff0c;但如果要追究其背后的一系列逻辑&#xff0c;可能是整个应用中最复杂的一个功能。今天主要实践目标&#xff0c;会抛开复杂的逻辑&#xff0c;尝试纯粹实现一个“搜索主页”&#xf…

STM32CubeIDE基础学习-USART串口通信实验(中断方式)

STM32CubeIDE基础学习-USART串口通信实验&#xff08;中断方式&#xff09; 文章目录 STM32CubeIDE基础学习-USART串口通信实验&#xff08;中断方式&#xff09;前言第1章 硬件介绍第2章 工程配置2.1 工程外设配置部分2.2 生成工程代码部分 第3章 代码编写第4章 实验现象总结 …

3D数据格式导出工具HOOPS Publish如何生成高质量3D PDF?

在当今数字化时代&#xff0c;从建筑设计到制造业&#xff0c;从医学领域到电子游戏开发&#xff0c;3D技术已经成为了不可或缺的一部分。在这个进程中&#xff0c;将3D模型导出为3D PDF格式具有重要的意义。同时&#xff0c;HOOPS Publish作为一个领先的解决方案&#xff0c;为…

Python算法学习

一、排序 排序算法是指将一组数据按照某种规则重新排列&#xff0c;使得数据呈现出递增或递减的顺序。常见的排序算法包括冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序等。 1.冒泡排序 解释&#xff1a; 冒泡排序通过不断交换相邻两个元素的位置&#xff0c;使…

仓库规划(plan)

明天就要考试了&#xff0c;但是我正处于一点都不想学的状态 高考前我也是这样的 逆天 代码如下&#xff1a; #include<vector> #include<cstdio> using namespace std; int n, m; struct Node{int id;vector<int> d;bool operator<(const Node &t…