【C++】vector基本接口介绍

news2024/11/28 4:06:48

vector接口目录:

一、vector的初步介绍

1.1vector和string的联系与不同

1.2 vector的源码参数

二、vector的四种构造(缺省+填充元素+迭代器+拷贝构造)

三、vecto的扩容操作与机制

3.1resize(老朋友了,不会就去看string ) && reserve

 3.2 reserve的扩容机制

3.3 vector和malloc分别实现动态开辟的二维数组

118. 杨辉三角 - 力扣(LeetCode)

四、三种遍历方式

4.1operator[ ]对于越界访问的检查机制(一段经典的代码错误)

4.2三种遍历方式 

 五、vector的修改操作

5.1 assign和迭代器的配合使用

5.2  insert和find(vector库中没有find)的配合使用

inset && find例子: 

5.3 类外、类内、算法库的3个swap(编译器费心良苦)

六、看源码时需要注意的问题


一、vector的初步介绍

1.1vector和string的联系与不同

1. vector底层也是用动态顺序表实现的,和string是一样的,但是string默认存储的就是字符串,而vector的功能较为强大一些,vector不仅能存字符,理论上所有的内置类型和自定义类型都能存,vector的内容可以是一个自定义类型的对象,也可以是一个内置类型的变量

2. vector在使用时需要进行类模板的实例化,因为传递的模板参数不同,则vector存储的元素类型就会有变化,所以在使用vector的时候要进行类模板的显式实例化


类模板的第二个参数是空间配置器,这个学到后面再说,而且这个参数是有缺省值的,我们只用这个缺省值就欧克了,所以在使用vector时,只需要关注第一个参数即可。

1.2 vector的源码参数


二、vector的四种构造(缺省+填充元素+迭代器+拷贝构造)

vector 提供了四种构造方式 – 无参构造、n 个 val 构造、迭代器区间构造以及拷贝构造

 

需要注意的是,迭代器区间构造是一个函数模板,即我们可以用其他类来构造 vector 对象:


三、vecto的扩容操作与机制

3.1resize(老朋友了,不会就去看string ) && reserve

1. 对于string和vector,reserve和resize是独有的,因为他们的底层都是动态顺序表实现的,list就没有reserve和resize,因为他底层是链表嘛

2. 对于reserve这个函数来说,官方并没有将其设定为能够兼容实现缩容的功能,明确规定这个函数在其他情况下,例如预留空间要比当前小的情况下,这个函数的调用是不会引起空间的重新分配的,也就是说容器vector的capacity是不会被影响的。

  

为什么它不会进行缩容?

因为我们在缩容的时候,不是简单的砍一刀后面丢掉,必须重新开辟空间,进行异地缩容。缩容表面看起来是降低了空间的使用率,想要提高程序的效率,但实际上并未提高效率,缩容是需要异地缩容的,需要重新开空间和拷贝数据,代价不小,所以平常不建议对空间进行缩容。reserve不缩容,其实也是一种空间换时间的操作

3. vector的resize和string的resize同样具有三种情况,但vector明显功能比string要更健壮一些,string类型只能针对于字符,而vector在使用resize进行初始化空间数据时,对内置类型和自定义类型均可以调用对应的拷贝构造来初始化,所以其功能更为健壮,默认将整型类型初始化为0,指针类型初始化为空指针。

4. 缺省值为T(),T的匿名对象。T有可能是自定义类型或者内质类型,可以认为内置类型有构造函数,编译器会特殊处理 

void test_vector()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	//resize和reserve对于vector和string是独有的,对于list而言,就没有reserve和resize

	cout << v.capacity() << endl;
	v.reserve(10);
	cout << v.capacity() << endl;
	v.reserve(4);
	cout << v.capacity() << endl;//并不会缩容,缩容并不会提高效率,缩容是有代价的,某种程度上就是以空间换时间。

	v.resize(8);//int、指针等这些内置类型的默认构造就把他们初始化为0和空指针这些。
	v.resize(15, 1);
	v.resize(3);//不缩容,也是采用惰性删除的方式,将size调整为3就可以了,显示数组内容的时候按照size大小显示就可以了。
}

 3.2 reserve的扩容机制

vector 的扩容机制和 string 的扩容机制是一样的,因为它们都是动态增长的数组:我的VSCode 下大概是 2倍扩容  ,Linux g++ 下是标准的二倍扩容,测试用例如下:

void Vector_Capacity() {
    vector<int> v;
	size_t sz=v.capacity();
	cout << "making v 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';
		}
	}
}

3.3 vector和malloc分别实现动态开辟的二维数组

118. 杨辉三角 - 力扣(LeetCode)

对于vector来讲的话,动态开辟就不需要我们自己做,通过resize就可以控制容器的空间大小,不用malloc动态开辟了,所以对于动态开辟的二维数组来讲,vector实际上要简便许多

 

 

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv;
        vv.resize(numRows);
        //第二个参数不传就是匿名对象,会自动调用容器中元素的构造函数。内置类型或自定义类型的构造。
        for(size_t i=0; i<vv.size(); i++)
        {
            vv[i].resize(i+1, 0);//给每一个vector<int>容器预留好空间并进行初始化
            vv[i][0] = vv[i][vv[i].size() - 1] = 1;
        }
        for(int i=0; i<vv.size(); i++)
        {
            for(int j=0; j<vv[i].size(); j++)
            {
                if(vv[i][j]==0)
                {
                    vv[i][j]=vv[i-1][j]+vv[i-1][j-1];
                }
            }
        }
        return vv;

    }
};

四、三种遍历方式

4.1operator[ ]对于越界访问的检查机制(一段经典的代码错误)

下面所展示的代码是比较经典的错误,就是我们用reserve扩容之后,就利用[ ]和下标来进行容器元素的访问(这是错误的),扩容之后空间的使用权确实属于我们,但是operator[ ]的越界访问检查机制,导致了我们程序的崩溃,assert(pos<size),所以对于元素的访问,是要用resize来进行size的调整的,而reserve的主要作用是用来提前预留空间,在空间不够使用的情况下进行调用,所

以这里使用的情景有些不搭。

4.2三种遍历方式 

for (size_t i = 0; i < v.size(); ++i)
	{
		cout << v[i] << " ";
	}
	cout << endl;
 vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
    for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

 五、vector的修改操作

5.1 assign和迭代器的配合使用

1. assign有两种使用方式

一种是用n个value来进行容器元素的覆盖

一种是用迭代器区间的元素来进行容器元素的覆盖,这里的迭代器采用模板形式,因为迭代器类型不仅仅可能是vector类型,也有可能是其他容器类型,所以这里采用模板泛型的方式

2. 其实迭代器使用起来实际是非常方便的,由于vector的底层是连续的顺序表,所以我们可以通过指针±整数的方式来控制迭代器赋值的区间,所以采用迭代器作为参数是非常灵活的

void test_vector5()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	v.assign(10, 1);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	vector<int> v1;
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);

	v.assign(v1.begin(), v1.end());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	string str="hello world";
    vector<char> vs;
	vs.assign(str.begin(), str.end());
	for (auto e : vs)
	{
		cout << e << " ";
	}
	cout << endl;

	vs.assign(++str.begin(), --str.end());
	for (auto e : vs)
	{
		cout << e << " ";
	}
	cout << endl;
}

5.2  insert和find(vector库中没有find)的配合使用

const value_type 就是const T

 

为什么find要使用左闭右开? (我认为有四点原因)

 4. 迭代器的end()也是指向最后一个元素的下一个位置,也是左闭右开

好的,我们继续回归正题

1.  对于顺序表这种结构来说,头插和头删的效率是非常低的,所以vector只提供push_back和pop_back,而难免遇到头插和头删的情况时,可以偶尔使用insert和erase来进行头插和头删,并且insert和erase的参数都使用了迭代器类型作为参数,因为迭代器更具有普适性

2. 如果要在vector的某个元素位置(在1,2等等具体位置插入倒也不需要find)进行插入时,肯定是需要使用 find接口 的,但其实vector的默认成员函数并没有find接口,这是为什么呢?因为大多数的容器都会用到查找接口,也就是find,所以C++直接将这个接口放到算法库里面去了,实现一个函数模板,这个函数的实现实际也比较简单,只要遍历一遍迭代器然后返回对应位置的迭代器即可,所以这个函数不单独作为某个类的成员函数,而是直接放到了算法库里面去。所以得加头文件

inset && find例子: 

例子一:

使用 iterator insert (iterator position, const value_type& val) 插入单个元素:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> myVector = {1, 2, 4, 5};
    std::vector<int>::iterator it = myVector.begin() + 2; // 指向位置 4 的迭代器

    myVector.insert(it, 3); // 在位置 4 插入元素 3

    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

例子二:

使用 void insert (iterator position, size_type n, const value_type& val) 插入多个相同的元素: 

#include <iostream>
#include <vector>

int main() {
    std::vector<int> myVector = {1, 2, 5};
    std::vector<int>::iterator it = myVector.begin() + 2; // 指向位置 5 的迭代器

    myVector.insert(it, 3, 4); // 在位置 5 插入 3 个元素值为 4 的元素

    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

例子三:

使用 template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last) 插入一个范围内的元素: 

#include <iostream>
#include <vector>
#include <iterator>

int main() {
    std::vector<int> myVector = {1, 2, 7};
    std::vector<int> newElements = {3, 4, 5};

    std::vector<int>::iterator it = myVector.begin() + 2; // 指向位置 7 的迭代器

    // 在位置 7 插入 newElements 中的所有元素
    myVector.insert(it, newElements.begin(), newElements.end());

    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

 例子四:(这个是知识点覆盖最多的)
利用 find 找到元素位置 并且插入

void test_vector6()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
    cout << "v的元素:" ;
    for (auto e : v)
	{
		cout << e << " ";
	}
    cout <<endl;
	v.insert(v.begin(), 4);
    cout << "第一次插入" << endl;
    for (auto e : v)
	{
		cout << e << " ";
	}
    cout << endl;
	v.insert(v.begin()+2, 4);
    cout << "第二次插入" << endl;
    for (auto e : v)
	{
		cout << e << " ";
	}
    cout << endl;
	// 没有find成员
	//vector<int>::iterator it = v.find(3);

    std::vector<int> myVector = {1, 2, 3, 4, 5};
    int target = 3;
    cout << "myvector的元素:" ;
    for (auto e : v)
	{
		cout << e << " ";
	}
    cout << endl;
    // 使用 std::find 在 vector 中查找元素 target
    std::vector<int>::iterator it = std::find(myVector.begin(), myVector.end(), target);
	if (it != myVector.end()) // 如果没找到的话 find返回的就是end 也就是元素的下一个位置
	{
		myVector.insert(it, 30);//这个if很重要
	}
    cout << "第三次插入" << endl;
	for (auto e : myVector)
	{
		cout << e << " ";
	}
	cout << endl;
}

5.3 类外、类内、算法库的3个swap(编译器费心良苦)

vector类内的swap用于两个对象的交换,在swap实现里面再调用std的swap进行内置类型的交换,但C++用心良苦,如果你不小心使用的格式是std里面的swap格式的话,也没有关系,因为类外面有一个匹配vector的swap,所以会优先调用类外的swap,C++极力不想让你调用算法库的swap,就是因为如果交换的类型是自定义类型的情况下,算法库的swap会进行三次深拷贝,代价极大,所以为了极力防止你调用算法库的swap,C++不仅在类内定义了swap,在类外也定义了已经实例化好的swap,调用时会优先调用最匹配的swap

void test_vector8()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	vector<int> v1;
	v1.swap(v);
	swap(v1, v);//一不小心这样用呢?那也不会去调用算法库里面的三次深拷贝的swap
	//这里会优先匹配vector的类外成员函数,既然有vector作为类型实例化出来的swap函数模板,就没有必要调用算法库里面的模板进行实例化
	//template <class T, class Alloc>
	//void swap(vector<T, Alloc>&x, vector<T, Alloc>&y);
}

 


六、看源码时需要注意的问题

1. 看源码框架的方法:将类成员变量先抽出来,看一看成员函数的声明具体都实现了什么功能,如果想要看实现,那就去.c文件抽出来具体函数去看

2. 看某些书籍时的道理和看源码是一样的,要进行抽丝剥茧,不要想着第一遍就把看到的所有东西都弄回,如果你觉得这本书或源码非常不错,你可以多次反复的去看,要循序渐进的去学,一段时间之后,你的知识储备上来之后,可能再去看书籍或者源码又有新的不同的感受,所以不要想着一遍就把所有的东西都搞明白,第一遍弄懂个70%-80%就很不错,如果你想学扎实一点,那就增加遍数。

 希望对大家有所帮助!!

祝大家中秋快乐!!

 

 

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

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

相关文章

Linux文件查找,别名,用户组综合练习

1.文件查看: 查看/etc/passwd文件的第5行 [rootserver ~]# head -5 /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologi…

epoll与socket缓冲区的恩恩怨怨

文章目录 前言一、什么是socket缓冲区二、阻塞与非阻塞内核缓冲区1、如果发送缓冲区满了会怎么样阻塞非阻塞 2、如果接受缓冲区为空会怎么样阻塞非阻塞 三、epoll与缓冲区的恩恩怨怨水平触发边缘触发非阻塞阻塞 结论 前言 本文深挖网络编程中的缓冲区&#xff0c;从什么是缓冲…

排序:基数排序算法分析

1.算法思想 假设长度为n的线性表中每个结点aj的关键字由d元组 ( k j d − 1 , k j d − 2 , k j d − 3 , . . . , k j 1 , k j 0 ) (k_{j}^{d-1},k_{j}^{d-2},k_{j}^{d-3},... ,k_{j}^{1} ,k_{j}^{0}) (kjd−1​,kjd−2​,kjd−3​,...,kj1​,kj0​)组成&#xff0c; 其中&am…

微信小程序开发基础(一)认识小程序

微信小程序&#xff0c;小程序的一种&#xff0c;英文名Wechat Mini Program&#xff0c;是一种不需要下载安装即可使用的应用&#xff0c;它实现了应用“触手可及”的梦想&#xff0c;用户扫一扫或搜一下即可打开应用。微信小程序是一种不用下载就能使用的应用&#xff0c;也是…

排序:外部排序算法分析

1.外存与内存之间的数据交换 1.外存&#xff08;磁盘&#xff09; 操作系统以“块”为单位对磁盘存储空间进行管理&#xff0c;如:每块大小1KB 各个磁盘块内存放着各种各样的数据。 2.内存 磁盘的读/写以“块”为单位数据读入内存后才能被修改修改完了还要写回磁盘。 2.外…

Purple-Pi-OH OHOS SDK编译手册

一、源码获取 1.1 源码获取 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;1234 $ mkdir purple-pi #将下载的ido_purple_pi_oh_ohos3.2_sdk.tgz拷贝到purple-pi $ cd purple-pi $ md5sum ido_purple_pi_oh_ohos3.2_sdk.tgz e6ca2d96aa7c628992ae0bbf4d14c2ca …

面试买书复习就能进大厂?

大家好&#xff0c;我是苍何。 现在进大仓是越来越难了&#xff0c;想通过简单的刷题面试背书&#xff0c;比几年前难的不少&#xff0c; 但也并非毫无希望&#xff0c;那究竟该如何准备才能有希望进大厂呢&#xff1f; 我总结了 4 点&#xff1a; 1、不差的学历背景 2、丰富…

EcmaScript标准-导入与导出-js

ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协会&#xff0c;European Computer Manufacturers Association&#xff09;通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛&#xff0c;它往往被称为JavaScript或JScript&#xff0c;所以它…

lwIP 开发指南(下)

目录 NETCONN 编程接口简介netbuf 数据缓冲区netconn 连接结构netconn 编程API 函数 NETCONN 编程接口UDP 实验NETCONN 实现UDPNETCONN 接口的UDP 实验硬件设计软件设计下载验证 NETCONN 接口编程TCP 客户端实验NETCONN 实现TCP 客户端连接步骤NETCONN 接口的TCPClient 实验硬件…

九、Delay函数

1、两个延时函数 vTaskDelay&#xff1a;至少等待指定个数的Tick Interrupt才能变为就绪态。vTaskDelayUntil&#xff1a;等待到指定的绝对时刻&#xff0c;才能变为就绪态。 2、函数原型 /* xTicksToDelay: 等待多少个Tick */ void vTaskDelay( const TickType_t xTicksToD…

1.(vue3.x+vite)封装组件

前端技术社区总目录(订阅之前请先查看该博客) 关联博客 2.(vue3.x+vite)组件注册并调用 1:创建组件目录package,并创建相关工程结构 2:编写组件内容(index.vue) 3:添加注册组件方法(index.js) 4:添加路由

QT按钮介绍

目录 按钮基类 QAbstractButton QPushButton QToolButton QRadioButton QCheckBox 按钮基类 QAbstractButton 这是按钮的基类&#xff0c;它是继承QWidget类 它可对当前的图标&#xff0c;标题等进行设置。 它有自己的一些信号与槽函数&#xff1a; /* 当按钮被激活时(即…

【C++的OpenCV】第十三课-OpenCV基础强化(一):绝对有用!Mat相关的一系列知识(基础->进阶)

&#x1f389;&#x1f389;&#x1f389; 欢迎各位来到小白 p i a o 的学习空间&#xff01; \color{red}{欢迎各位来到小白piao的学习空间&#xff01;} 欢迎各位来到小白piao的学习空间&#xff01;&#x1f389;&#x1f389;&#x1f389; &#x1f496;&#x1f496;&…

rhel8 网络操作学习

一、查询dns服务器地址汇总 1.查询dns服务器地址&#xff1a; &#xff08;1&#xff09;方法一&#xff1a;执行命令 cat /etc/resolv.conf 执行结果如下&#xff1a; nameserver后面就是dns服务器的ip地址。 &#xff08;2&#xff09;方法2&#xff1a;查看/etc/syscon…

Linux性能优化--性能工具-系统CPU

2.0.概述 本章概述了系统级的Linux性能工具。这些工具是你追踪性能问题时的第一道防线。 它们能展示整个系统的性能情况和哪些部分表现不好。 1.理解系统级性能的基本指标&#xff0c;包括CPU的使用情况。 2.明白哪些工具可以检索这些系统级性能指标。2.1CPU性能统计信息 为了…

基于单片机的煤气泄漏检测报警装置设计

一、项目介绍 煤气泄漏是一种常见的危险情况&#xff0c;可能导致火灾、爆炸和人员伤亡。为了及时发现煤气泄漏并采取相应的安全措施&#xff0c;设计了一种基于单片机的煤气泄漏检测报警装置。 主控芯片采用STM32F103C8T6作为主控芯片&#xff0c;具有强大的计算和控制能力。…

《PPT 自我介绍》:一本让你的职场表现更加出色的秘籍?

这里提供一个2000字左右的PPT自我介绍模板制作指南&#xff1a; 自我介绍是面试或工作中常见的情况&#xff0c;利用PPT可以给人留下更深刻的印象。但如何快速且专业地制作一个自我介绍PPT呢?这里给大家介绍几点技巧&#xff1a; 1. 选择一个简洁大方的PPT模板 首先要选择一…

最新AI创作系统源码ChatGPT源码+附详细搭建部署教程+AI绘画系统+支持国内AI提问模型

一、AI系统介绍 SparkAi创作系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&am…

中秋海报制作不求人,详细教程来袭

中秋节是我国传统的节日&#xff0c;也是家庭团聚的重要时刻。在节日即将到来之际&#xff0c;如何设计一张温馨、美丽的中秋节海报&#xff0c;让家庭成员感受到节日的氛围和温暖呢&#xff1f;下面就为大家分享一些中秋节海报设计的秘诀。 一、选择合适的模版 登录乔拓云后&a…

NSSCTF做题(4)

[NISACTF 2022]checkin 简单的一道代码审计了 但是发现传参传不上去 后来发现 在选中nisactf的时候&#xff0c;注释里面的内容也被标记了 不知道是为什么&#xff0c;把它复制到010里边去看看 发现了不对的地方 nisactf应该传参 根据这个进行url编码 我们选择实际的参名和…