深入理解vector 【C++】

news2025/1/14 18:31:11

                               

一、vector的介绍:

        1.vector是表示可变大小的顺序容器。

        2.就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自 动处理。

        3.本质讲,vector使用动态分配数组来存储它的元素。当空间满了并且插入新元素的时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。

        4.vector分配空间策略:我们一般会按照两倍进行扩容来避免频繁扩容。

        5.vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增 长。

        6.与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其他不在末尾的删除和插入操作,效率更低。比起list和forward_list 统一的迭代器和引用更好。

总结:它底层实际上和顺序表差不多 可以使用下标来访问 为了提高效率扩容一般是按照两倍扩容

二、vector的定义:

方法一: 构造一个某类型的空容器

        vector<int> v1; // 构造一个int类型的空容器

方法二:构造一个含有n个val值的容器

        vector<int> v2(10, 2); // 构造一个int类型的容器 初始化102

方式三:拷贝构造

        vector<int> v3(v2);

方式四:使用迭代器拷贝

        vector<int> v4(v2.begin(), v2.end()); //迭代器拷贝构造

注:

1.使用别的容器的迭代器也可以拷贝(不是vector 以string为例)

        string s1("hello world");
        vector<char> v5(s1.begin(), s1.end()); // 使用string类的迭代器构造

 2.vector也可以嵌套自己,vector<vector<int>>相当于二维数组

        第一个vector中的指针指向了一块存放vector<int>类型的空间,而之后的每一个vector中的指针又指向存放int类型的空间。(就相当于二维数组,只不过这个是动态的并且有成员函数

三、空间相关

3.1 size

size_type就是size_t

   返回容器中占用的元素个数 

#include <iostream>
#include <vector>

int main()
{
	std::vector<int> myints;
	std::cout << "0. size: " << myints.size() << '\n';

	for (int i = 0; i < 10; i++) myints.push_back(i);
	std::cout << "1. size: " << myints.size() << '\n';

	myints.insert(myints.end(), 10, 100);   //从最后的位置开始插入10个100
	std::cout << "2. size: " << myints.size() << '\n';

	myints.pop_back();
	std::cout << "3. size: " << myints.size() << '\n';

	return 0;
}

   

3.2 capacity

size_type就是size_t

#include <iostream>
#include <vector>

int main()
{
	std::vector<int> myvector;

	// set some content in the vector:
	for (int i = 1; i <= 100; i++)
	{
		myvector.push_back(i);
		if (i % 10 == 0)
		{
			std::cout << "size: " << (int)myvector.size() << '\n';
			std::cout << "capacity: " << (int)myvector.capacity() << '\n';
		}
	}
	return 0;
}

 

        VS保持一贯的作风,先有一段空间存放数据,超了之后按1.5倍扩容。

3.3 empty

        判断vector是否为空(size是否为0)  为空就返回true      不为空返回false

3.4 resize

        resize函数可以改变容器的有效元素个数

                   value_type就是模板类型T ,不传的话,就会去调用对应的 T() 构造函数

使用规则是这样子的

  1. 假设所给值小于当前容器size值 size缩小至所给值
  2. 假设所给值大于当前容器size值并且小于capacity值 size扩大至所给值 扩大的范围赋值(调用对应的构造函数
  3.  假设所给值大于当前容器size值并且大于capacity值 先扩容 后面操作和2一样

 调用对应的构造函数:

    C++规定:内置类型也有对应的构造函数

        int -->  0

        double -->0.0

        int*  -->nullptr

    对应自定义类型,调用对应的构造函数 

// resizing vector
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector;

    // set some initial content:
    for (int i = 1; i < 10; i++) myvector.push_back(i);

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

    std::cout << "myvector contains:";
    for (int i = 0; i < myvector.size(); i++)
        std::cout << ' ' << myvector[i];
    std::cout << '\n';

    return 0;
}

3.5 reserve

 

  使用规则如下

1.假设所给值小于当前容量值 什么都不做

2 假设所给值大于当前容量值 扩容至所给值

// vector::reserve
#include <iostream>
#include <vector>

int main()
{
    std::vector<int>::size_type sz;//std全局域 vector<int>类 typedef的size_type  就是 size_t
    std::vector<int> foo;
    sz = foo.capacity();
    std::cout << "making foo grow:\n";
    for (int i = 0; i < 100; ++i) {
        foo.push_back(i);
        if (sz != foo.capacity()) {
            sz = foo.capacity();
            std::cout << "capacity changed: " << sz << '\n';
        }
    }

    std::vector<int> bar;
    sz = bar.capacity();
    bar.reserve(100);   // this is the only difference with foo above
    std::cout << "making bar grow:\n";
    for (int i = 0; i < 100; ++i) {
        bar.push_back(i);
        if (sz != bar.capacity()) {
            sz = bar.capacity();
            std::cout << "capacity changed: " << sz << '\n';
        }
    }
    return 0;
}

reserve会先把空间括好,你想要括多少,就传参。 

四、迭代器相关

在前面的string类里面已经讲过迭代器了。这里我就贴一下代码就过了。后面的insert和erase迭代器失效才是重头戏!

// vector::reserve
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 构造一个int类型的容器
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }

    //正向迭代器
    vector<int>::iterator it = v1.begin();
    while (it != v1.end())
    {
        cout << *it << " ";
        it++;
    }

    cout << endl;

    //反向迭代器
    vector<int>::reverse_iterator rit = v1.rbegin();
    while (rit != v1.rend())
    {
        cout << *rit << " ";
        rit++;
    }
}

 

五、访问数据 

 

5.1 operator[] 对[]运算符进行了重载,一个通过下标来访问元素。

5.2 at和operator[] 一样,通过下标来访问。

5.3 front 访问第一个元素

5.4 back 访问最后一个元素(v.size()-1的下标的元素)

5. data 用于返回指向vector中第一个元素的指针

// vector::data
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector(5);

    int* p = myvector.data();

    *p = 10;
    ++p;
    *p = 20;
    p[2] = 100;

    std::cout << "myvector contains:";
    for (unsigned i = 0; i < myvector.size(); ++i)
        std::cout << ' ' << myvector[i];
    std::cout << '\n';

    return 0;
}

 可以通过p指针来访问vector类中的元素

六、增删查改 

增:

5.1 push_back  向尾部插入一个数据

vector<int> v1;

v1.push_back(1);

v1.push_back(2);

v1.push_back(3); 

就存放了1 2 3三个数据到v1对象中

5.2 insert 指定位置插入数据

 第一种:   (返回值是iterator,在之后讲迭代器失效会提到)

        选择一个位置(迭代器),选择要插入的数据。

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.insert(v1.begin(), 100); // 我们再开头插入一个元素100
    vector<int>::iterator it = v1.begin();
    while (it != v1.end())
    {
        cout << *it << " ";
        it++;
    }
	return 0;
}

第二种:

        选择一个位置(迭代器),选择要插入的个数,选择要插入的数据。

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.insert(v1.begin(), 10, 5); // 我们头插100个5
    vector<int>::iterator it = v1.begin();
    while (it != v1.end())
    {
        cout << *it << " ";
        it++;
    }
	return 0;
}

 

删:

5.2 pop_back  从尾部删除一个数据

        

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    for (auto u : v1)
    {
        cout << u << ' ';
    }
    cout << endl;

    v1.pop_back();
    v1.pop_back();
    v1.pop_back();
    for (auto u : v1)
    {
        cout << u << ' ';
    }
    cout << endl;

	return 0;
}

5.3 erase 指定位置删除数据

注意这里的返回值是iterator,在之后讲迭代器失效的时候会提到。 

第一种:删除一个指定位置的元素

第二种:删除一段区间的元素

// erasing from vector
#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> myvector;

  // set some values (from 1 to 10)
  for (int i=1; i<=10; i++) myvector.push_back(i);

  // erase the 6th element
  myvector.erase (myvector.begin()+5);

  std::cout << "myvector contains:";
  for (unsigned i=0; i<myvector.size(); ++i)
    std::cout << ' ' << myvector[i];
  std::cout << '\n';

  // erase the first 3 elements:
  myvector.erase (myvector.begin(),myvector.begin()+3);

  std::cout << "myvector contains:";
  for (unsigned i=0; i<myvector.size(); ++i)
    std::cout << ' ' << myvector[i];
  std::cout << '\n';

  return 0;
}

查:

5.4   find

 find函数可以找到容器中我们要寻找的值 并且返回迭代器

 find函数有三个参数 迭代器左区间 迭代器右区间 要查找元素 (左闭右开)适用于所有的迭代器。

 为什么string类里面要自己设计一个find函数呢?因为要满足对各种字符串查找的需求。 

        注意!!!!!!

        这里的find函数的头文件是 algorithm !!!!!!!!!

        一定要记住! 不是容器的默认函数

#include <iostream>
#include <vector>
#include<algorithm>//为了能够使用find函数
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    // v1.erase(v1.begin(), v1.begin()+2);
    vector<int>::iterator pos = find(v1.begin(), v1.end(), 3);  // 找到3
    v1.erase(pos); // 删除3
    vector<int>::iterator it = v1.begin();
    while (it != v1.end())
    {
        cout << *it << " ";
        it++;
    }
	return 0;
}

 

改:

operator=

// vector assignment
#include <iostream>
#include <vector>
using namespace std;
int main()
{
	std::vector<int> foo(3, 1);
	std::vector<int> bar(5, 2);

	bar = foo;
	foo = std::vector<int>();

    //打印foo中的元素
	for (auto u : foo)
	{
		cout << u << " ";
	}
	cout << endl;
	std::cout << "Size of foo: " << int(foo.size()) << '\n';


    //打印bar中的元素
	for (auto u : bar)
	{
		cout << u << " ";
	}
	cout << endl;
	std::cout << "Size of bar: " << int(bar.size()) << '\n';
	return 0;
}

 这个运算符会改变size大小和内部的变量

七、迭代器失效: 

在使用insert和earse的时候,会出现迭代器失效的问题。

 7.1  实例1:
#include <iostream>
#include <vector>
using namespace std;
void test_1()
{
    vector<int> vec;
    vec.push_back(0);
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    vec.push_back(4);

    cout << vec.size() << ":" << vec.capacity() << endl;
    vector<int>::iterator it = vec.begin();
    while (it != vec.end())
    {
        if (*it % 2 == 0)
        {
            vec.insert(it, 5);   //这里会出现错误
            ++it;
        }
        ++it;
    }
    cout << vec.size() << ":" << vec.capacity() << endl;
    for (auto e : vec)
    {
        cout << e << " ";
    }
    cout << endl;
}
int main()
{
    test_1();
    return 0;
}

分析:

vec.insert(it, 5);   //这里会出现错误   当你把it传进去的时候,是传值传进去的,

它在insert内部会进行扩容(异地扩容)操作。内部的pos指针还指向原地,就不再准确了。

就会造成野指针问题。

而如果传引用传递的话,会让其他一些函数不能使用。

原因:insert之后vector会分配新的空间,旧迭代器便会失效。

解决方法:取insert函数的返回值为新的迭代器。

 用图片来帮助理解:

      

         

改法:(接收新的it作为返回值)

while (it != vec.end())
{
    if (*it % 2 == 0)
    {
        it=vec.insert(it, 5);//这样子写是错误的,应该怎么改?
        it++;
    }
    ++it;
    
}

7.2   实例2:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.push_back(5);
    v1.push_back(6);
    vector<int>::iterator it = find(v1.begin(), v1.end(), 2); // 意思是我们要删除2
    v1.erase(v1.begin()); //删除头元素
    v1.erase(it); //删除元素2
    return 0;
}

调试看看:

 咦?你看到了没有,it指针指向的位置还是那个位置,但是里面的值却改变了。

删除头元素,就是把数据向前挪动,而it指针指向不变。导致后面出问题了。

解决方法:同样在使用完erase之后,拿it去接收erase函数的返回值

7.3  总结(重点)

1.     insert和erase都会导致迭代器失效,我们都需要用it来接收新的迭代器。

        it=insert(it,5);  //在it位置插入元素5,拿it来接收新迭代器

        it=erase(it);//删除it位置的元素,拿it来接收新迭代器

2.   insert的返回值的it -->指向新插入的元素

     erase的返回值的it  -->指向删除元素的下一个元素


 本篇内容到此结束!感谢大家阅读!!!

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

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

相关文章

国产化改造之容器迁移指导(未完)

一、背景 信创即信息技术应用创新的简称,涵盖了国产软件、国产芯片以及云计算等各个方向,也可以理解为常说的“ZZKK(自主可控)”, ZZKK是指对国内企事业单位应用系统中关键软硬件部件的安全性、可靠性、性能稳定性、安全接入等方面进行评估和测试的过程。信创的发展核心就…

一对一WebRTC视频通话系列(二)——websocket和join信令实现

本系列博客主要记录WebRtc实现过程中的一些重点&#xff0c;代码全部进行了注释&#xff0c;便于理解WebRTC整体实现。 一对一WebRTC视频通话系列往期博客&#xff1a; 一对一WebRTC视频通话系列&#xff08;一&#xff09;—— 创建页面并显示摄像头画面 websocket和join信令…

下载Node.js及其他环境推荐nvm

文章目录 项目场景&#xff1a;下载Node.js环境配置配置环境变量 安装脚手架安装依赖安装淘宝镜像安装 cnpm&#xff08;我需要安装&#xff09;nvm 安装 Node.js &#xff08;推荐&#xff09; 项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 项目…

Java毕业设计 基于SSM SpringBoot vue宠物领养平台

Java毕业设计 基于SSM SpringBoot vue宠物领养平台 SSM 宠物领养平台 功能介绍 首页 图片轮播 新闻信息 新闻类型 新闻详情 宠物百科 宠物百科类型 宠物百科详情 宠物 宠物类型 宠物详情 立即领养 留言 论坛 发布帖子 登录 个人中心 宠物收藏 宠物领养订单 后台管理 登录注…

pymeshlab创建给定水平半径、垂直半径和水平细分以及垂直细分的圆环并保存(torus)

一、关于环境 请参考&#xff1a;pymeshlab遍历文件夹中模型、缩放并导出指定格式-CSDN博客 二、关于代码 本文所给出代码仅为参考&#xff0c;禁止转载和引用&#xff0c;仅供个人学习。 # pymeshlab需要导入&#xff0c;其一般被命名为ml import pymeshlab as ml# 首先需…

TCP重传机制——快速重传

TCP 有一种快速重传机制&#xff0c;它不以时间为驱动&#xff0c;而是以数据驱动重传。 在上图&#xff0c;发送方发出了 1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5 份数据&#xff1a; 第一份 Seq1 先送到了&#xff0c;于是就 Ack 回 2&#xff1b;结果 Seq2…

Nutch库入门指南:利用Java编写采集程序,快速抓取北京车展重点车型

概述 在2024年北京车展上&#xff0c;电动汽车成为全球关注的焦点之一。这一事件不仅吸引了全球汽车制造商的目光&#xff0c;也突显了中国市场在电动汽车领域的领先地位。117台全球首发车的亮相&#xff0c;其中包括30台跨国公司的全球首发车和41台概念车&#xff0c;彰显了中…

buuctf-misc-26.后门查杀

26.后门查杀 题目&#xff1a;火绒D盾查杀关键文件获取flag 下载完文件&#xff0c;我们可以用火绒进行查杀后门&#xff0c;一般解压后&#xff0c;火绒会自动查杀到病毒文件 病毒查杀-自定义查杀 找到了需要注意的文件&#xff0c;用vscode打开这个html,可以发现和md5比较相…

【副本向】Lua副本逻辑

副本生命周期 OnCopySceneTick() 子线程每次心跳调用 --副本心跳 function x3323_OnCopySceneTick(elapse)if x3323_g_IsPlayerEnter 0 thenreturn; -- 如果没人进入&#xff0c;则函数直接返回endif x3323_g_GameOver 1 thenif x3323_g_EndTick > 0 thenx3323_CountDown…

Vue进阶之Vue项目实战(一)

Vue项目实战 项目搭建初始化eslint版本约束版本约束eslint配置 stylelintcspellcz-githusky给拦截举个例子 zx 项目搭建 node版本&#xff1a;20.11.1 pnpm版本&#xff1a;9.0.4 初始化 vue3最新的脚手架 pnpm create vite byelide-demo --template vue-ts pnpm i pnpm dev…

020、Python+fastapi,第一个Python项目走向第20步:ubuntu 24.04 docker 安装mysql8、redis(一)

系列文章 pythonvue3fastapiai 学习_浪淘沙jkp的博客-CSDN博客https://blog.csdn.net/jiangkp/category_12623996.html 前言 docker安装起来比较方便&#xff0c;不影响系统整体&#xff0c;和前面虚拟环境有异曲同工之妙&#xff0c;今天把老笔记本T400拿出来装了个ubuntu24…

【分布式系统】FLP、CAP、BASE、ACID理论简介

分布式系统一致性模型 在说FLP&#xff0c;CAP&#xff0c;BASE&#xff0c;ACID理论前&#xff0c;必须先说说分布式系统的一致性模型&#xff0c;它是其他理论的基础知识。 依次介绍几个相关的概念&#xff1a; 分布式系统是由多个不同的服务节点组成&#xff0c;节点与节…

VMware虚拟机安装Linux(CentOS)【超详细】

参考大佬文章&#xff1a;VMware虚拟机安装Linux教程(超详细)_vmware安装linux虚拟机-CSDN博客 目录 一、获取映射文件 二、新建虚拟机 三、安装操作系统 四、切换系统用户 一、获取映射文件 参考大佬文章获取映射文件&#xff0c;以及对应修改后缀名的方法 二、新建虚拟…

电阻 电容 电感

电阻理论基础 电阻定义 电阻决定式 温度对电阻的影响 一般电阻都是在-200-500ppm这个范围内 电阻选型 贴片电阻的标值 数字位数 3位和4位 字母R 除了数字和字母R的其他标注 需要查表 电阻精度 电阻功率和温度的关系 电阻的额定电压 零欧姆电阻 零欧姆电阻又称为跨…

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(三)

本系列课程&#xff0c;将重点讲解Phpsploit-Framework框架软件的基础使用&#xff01; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; 继续接上一篇文章内容&#xff0c;讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 当我们点击 submit 提…

【云原生】Docker 实践(四):使用 Dockerfile 文件的综合案例

【Docker 实践】系列共包含以下几篇文章&#xff1a; Docker 实践&#xff08;一&#xff09;&#xff1a;在 Docker 中部署第一个应用Docker 实践&#xff08;二&#xff09;&#xff1a;什么是 Docker 的镜像Docker 实践&#xff08;三&#xff09;&#xff1a;使用 Dockerf…

【多数组合 数学 字符串】2514. 统计同位异构字符串数目

本文涉及知识点 多数组合 数学 字符串 LeetCode2514. 统计同位异构字符串数目 给你一个字符串 s &#xff0c;它包含一个或者多个单词。单词之间用单个空格 ’ ’ 隔开。 如果字符串 t 中第 i 个单词是 s 中第 i 个单词的一个 排列 &#xff0c;那么我们称字符串 t 是字符串…

Web Storage 笔记12 操作购物车

相关内容&#xff1a;购物车实例 WebStorage存储空间足够大&#xff0c;访问都在客户端(Client)完成。有些客户端先处理或检查数据&#xff0c;就可以直接使用WebStorage进行存储&#xff0c;不仅可以提高访问速度&#xff0c;还可以降低服务器的练习。负担。例如&#xff0c;购…

如何访问公司内网?

访问公司内网是现代企业中的一个重要需求。无论是员工在外办公&#xff0c;还是远程技术支持&#xff0c;都需要能够安全、稳定地访问公司内部的网络资源。为了解决这一问题&#xff0c;北京金万维科技有限公司自主研发了一款名为【天联】的组网产品。 【天联】组网是一款异地组…

Linux下Palabos源码编译安装及使用

目录 软件介绍 基本依赖 其它可选依赖 一、源码下载 二、解压缩&#xff08;通过方式1下载源码.zip格式&#xff09; 三、编译安装 3.1 自带算例 ​编辑3.2 自行开发算例 四、简单使用 4.1 串行运行 4.2 并行运行 4.3 查看结果 软件介绍 Palabos是一款基于LBM&…