【C++】SLT——Vector详解

本片要分享的是关于STL中Vector的内容,Vector的内容于string非常相似,只要会使用string那么学习Vector时会非常流畅。

目录

1.vector介绍 

2.vector的简单实用

2.1.简单的无参构造

 ​编辑2.2.简单带参构造

2.3.迭代器区间初始化

2.4.vector的遍历

2.5.vector插入数据

2.6.扩容机制 

不同平台扩容机制

reverse

resize


1.vector介绍 

 官方的简介是vector是由一个动态增长数组实现的顺序容器,其实再简称一点就是顺序表。

以下是vector的组件

默认成员函数

 迭代器

与容量相关的函数

 与访问数据相关的函数

 与修改容器数据相关的

 可以看到上面有我们在string中就接触过的一些函数,比如push_back,下标访问时的operator[],测量长度的size,等等

所以这也是我们学习vector比较容易的原因,同时vector在设计上也基于string有了一些改进,在内部函数的设计方面也更加合理了一些;

2.vector的简单实用

2.1.简单的无参构造

我们上代码来观察,先从最最简单的初始化构造来开始

如上是一个vector的无参构造 

 首先我们需要包含vector的头文件

其次我们在定义的时候需要将容器实例化, 就是规定我们的数据类型;

 
2.2.简单带参构造

#include<iostream>
#include<vector>
using namespace std;
void test_vector1()
{
	vector<int> v1; 
	vector<int> v2(10,0); 
}
int main()
{

}

观察我们构造的v2,其中有两个参数,那这样的带参构造就是开辟是个空间,并且都初始化为0。

对比C语言我们不仅需要开一个数组,还需要memset,非常的麻烦;

2.3.迭代器区间初始化

#include<iostream>
#include<vector>
using namespace std;
void test_vector1()
{
	vector<int> v1; 
	vector<int> v2(10,0); 
	vector<int> v3(v2.begin(), v2.end());
 }
int main()
{
	test_vector1();
	return 0;
}

观察v3的初始化方式,我们使用了v2的迭代器的起始位置和末尾的位置

以上是相同容器的迭代器区间的初始化构造,那不同容器之间的初始化构造呢;

#include<iostream>
#include<vector>
using namespace std;
void test_vector1()
{
	vector<int> v1; 
	vector<int> v2(10,0); 
	vector<int> v3(v2.begin(), v2.end());
	string s("hello world");
	vector<int> v4(s.begin(), s.end());

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

那当然也是可以的,可以看到我们在初始化v4的时候使用了字符串s的区间,也能完成初始化,但是我们需要注意的是这里的底层涉及到隐式类型转换,所以才能初始化成功。

2.4.vector的遍历

我们在上面的介绍中就可以看到vector读取数据时可以采用[],我们不妨将初始化后的v3进行遍历

void test_vector2()
{
	vector<int> v2(10, 0);
	vector<int> v3(v2.begin(), v2.end());
	for (size_t i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << ' ';
	}
	cout << endl;
}
int main()
{
	test_vector2();
	return 0;
}

以下 是输出结果

 可以看到我们使用v2迭代器区间初始化的v3输出的结果和我们想要的结果相同。

这里使用了for循环和[ ]对数据进行读取,还可以使用迭代器进行访问。

void test_vector2()
{
	vector<int> v2(10, 0);
	vector<int> v3(v2.begin(), v2.end());
	for (size_t i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << ' ';
	}
	cout << endl;

	vector<int>::iterator it = v3.begin();
	while (it != v3.end())
	{
		cout << *it << ' ';
		++it;
	}
	cout << endl;
}

同样的我们首先要在vector类中声明并定义迭代器it,将v3的begin的位置给it,在while循环中依次将it解引用并输出,再对it进行++迭代,此时就完成了迭代器的遍历;

 第一行的输出结果为for循环;第二行的结果为迭代器,他们都可以进行遍历;

在这里需要提醒大家的是迭代器中的begin或是end等等是指向数据的位置

 所以这里我们可以将迭代器的功能理解为指针,遍历时将其解引用即可得到数据。

2.5.vector插入数据

如下是涉及到修改内容的函数

首先是尾插(push_back)

void test_vector8()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (auto e : v)
	{
		cout << e << ' ';
	}
	cout << endl;
}
int main()
{
	test_vector8();
	return 0;
}

 

结果不出所料,和我们在string中学到的插入方式相同;

同样的也有中间插入的方式insert, 

可以看到有很多种插入的方式,这里简单使用

 可以看到在begin 的位置之前插入了0;

那如何在以上数据的3的前面插入想要的数呢?

auto it = find(v.begin(), v.end(), 3);
	if (it != v.end());
	{
		v.insert(it, 30);
	}
	for (auto e : v)
	{
		cout << e << ' ';
	}
	cout << endl;

这里我们就需要通过find来配合使用insert函数来查找并插入,结果如上;

2.6.扩容机制 

不同平台扩容机制

接下来是有关容量的函数

我们首先来研究vector的扩容机制;

在string中我们了解到可以插入很多数据时,系统会自动扩容,在vector中也同样如此;我们用代码来了解一下vector的扩容机制

void test_vector5()
{
	size_t sz;
	vector<int> v;
	sz = v.capacity();
	for (size_t i = 0; i < 100; i++)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed" << sz << endl;
		}
	}
}

可以看到我我们在循环中给v插入数据,如果v的最大容量和插满数据时相同,系统就会自动扩容,此时我们再改变容量,并且标识出容量已经改变,那此时运行结果会是怎样呢?

 我们可以看到大概是以原先容量的1.5倍进行扩容。

同样的代码我们在Linux系统下使用g++编译会有什么效果

 可以看到对比vs环境下的1.5倍扩容,g++使用的是二倍扩容

reverse

其中还有reserve函数,他的作用是开辟空间,还是如上代码,我们使用reserve尝试一下

void test_vector6()
{
	size_t sz;
	vector<int> v;
	v.reserve(100);
	sz = v.capacity();
	for (size_t i = 0; i < 100; i++)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed" << sz << endl;
		}
	}
}

我们在它扩容之前使用reserve函数提前开好空间,运行结果如下

 可以看到之前的扩容过程都不见了,原因是我们在扩容之前使用了reverse提前开了空间,将capacity修改成我们想要的,即可跳过在for循环中一边插入数据一边扩容的情况。

resize

resize不同于reserve的是,resize不仅可以改变capacity的大小,同时也可以改变size的大小,还是上段同样的代码,这里将reserve修改为resize来观察结果,

resize不仅可以修改容量的大小,还可以修改其本身的长度,这里的运行结果是在resize后的数据再进行插入,也就是说将两端数据接到一起。

还需要注意的一种情况如下

我们使用reserve直接开辟空间,然后直接去访问(利用for循环插入数据) reserve开辟的空间并打印是否可行呢?

结果是不行的,原因是这里访问v使用的是[ ],而在[ ]之前的模拟实现中,也就是它的底层逻辑是有assert断言的,条件是访问的下标必须小于size,而reserve只能修改capacity,不能修改size,所以就会报错。 

那使用resize即修改size也修改capacity会怎样呢?

 可以看到这样操作就很丝滑了。

以上就是本次要分享的内容,在vector中还有一些不常用的函数在这里没有深入分析到,还请感兴趣的同学们自行尝试,如果对你有所帮助还请多多三连支持,感谢您的阅读。

猜你喜欢

转载自blog.csdn.net/wangduduniubi/article/details/132528083