在这里插入图片描述

1.什么是vector

详情点击蓝字跳转至官方定义
说白了,就是线性表中的顺序表,本质上是一个数组。

2.vector容器的接口:

在这里插入图片描述

今天我们主要来讲讲vector接口的使用,下一期将带大家深入探讨vector的底层原理,当然这里面有许多与string相似的地方,就不再赘述了~

2.vector的几种构造方式

2.1几种构造方式的调试:

在这里插入图片描述
在这里插入图片描述

  • default 默认构造(缺省)
  • fill 构造,并初始化
  • range 区间
  • cpy 拷贝构造
  • explicit 中文意思:明确的;放在构造函数之前表示不可使用参数构造的隐士类型转换。
  • template 模板,使之兼容性增强
  • initializer 一种类

看不懂没关系,下边有示例讲解~

在调试几种构造方式之前,我们得先写一个打印vector的函数,因为vector不支持流输入输出。
我们有很多种实现打印功能的方式~为了调试方便,我们只保留一种。

void Print(const vector<int>& v)
{
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;

	/*for (auto e : v)//有迭代器,支持范围for
	{
		cout << e << " ";
	}
	cout << endl;

	vector<int>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;*/
}

调试结果:

void test_vector1()
{
	vector<int> v1;//调用默认构造函数
	vector<int> v2(10, 1);//调用fill对应的初始化构造,将vector里的十个元素全部初始化成1
	vector<int> v3(v2.begin(), v2.end());//调用range对应的构造函数,用迭代器之间的元素来初始化v3
	string s1("xxxxxxxxxxxxx");
	vector<int> v4(s1.begin(), s1.end());//模板使区间构造更加灵活,不过它会将string元素对应的字符换成ASCII值
	vector<int> v5(v3);//拷贝构造
	Print(v2);
	Print(v3);
	Print(v4);
	Print(v5);
}

调试结果:

在这里插入图片描述
还有另一种构造方式,非常非常好用~但是因为涉及到一个特殊的类:initializer_list,我们单独拎出来讲,

2.2initializer_list类

点击跳转
在这里插入图片描述
它的成员函数:
在这里插入图片描述
我们发现它有迭代器!那就支持范围for

	auto il = { 10, 20, 30,1,2,2};
	for(auto e : il)
	{
		cout << e << " ";
	}
	cout << endl;

先放着,等会来试试~

在这里插入图片描述
本质上是讲{ }里的元素作为参数,构造出一个myclass对象,然后赋值给foo

调试代码:

void test_vector1()
{
	vector<int> v1;
	vector<int> v2(10, 1);
	vector<int> v3(v2.begin(), v2.end());
	string s1("xxxxxxxxxxxxx");
	vector<int> v4(s1.begin(), s1.end());
	vector<int> v5(v3);

	//vector<int> v6({ 1,2,3,4,5 });
	vector<int> v6 = { 1,2,3,4,5 };
	vector<int> v7 = { 1,2,3,4,5,1,1,1,1,1,1 };
	Print(v2);
	Print(v4);
	Print(v6);
	Print(v7);
	auto il = { 10, 20, 30,1,2,2};
	for(auto e : il)
	{
		cout << e << " ";
	}
	cout << endl;
}

运行结果:

在这里插入图片描述
非常方便好用~

3.vector的扩容机制

手动扩容我们用reserve接口或者resize接口,区别在于resize会将扩出来的部分按照指定值初始化,二者都不可缩容。
我们正常尾插,vector会自动扩容,正常是以1.5倍来扩容,(有舍入,不一定是整好的1.5倍)

调试代码

void test_vector2()
{
	vector<int> v1;
	const int n = 100;
	size_t old = v1.capacity();
	cout << v1.capacity() << endl;
	for (size_t i = 0; i < n; i++)
	{
		v1.push_back(i);
		if (old != v1.capacity())
		{
			cout << v1.capacity() << endl;
			old = v1.capacity();
		}
	}
	vector<int> v2;
	v2.resize(100, 1);
	Print(v2);
}

调试结果:

在这里插入图片描述

手动扩容相比于自动扩容,代价会小一点,所有如果提前知道需要多大容量的话,最好就一次性开好。

	vector<int> v1;
	//const int n = 100000000;
	v1.reserve(n);
	size_t begin = clock();
	for (size_t i = 0; i < n; i++)
	{
		v1.push_back(i);
	}
	size_t end = clock();
	cout << end - begin << endl;
	vector<int> v2;

有兴趣的同学可以回头用上述代码试一试,手动和自动,在亿次级别的扩容时长的差距。

4.vector的元素访问

同string的元素访问接口差不太多,这里就写个指定位置插入删除的示例代码,大家看看就行~

调试代码

void test_vector3()
{
	vector<int> v1 = { 1,2,3,4,5 };
	v1.push_back(6);
	Print(v1);

	// ͷ
	v1.insert(v1.begin(), 0);
	Print(v1);

	v1.insert(v1.begin()+3, 0);
	Print(v1);

	// ͷɾ
	v1.erase(v1.begin());
	Print(v1);

	v1.erase(v1.begin()+3);
	Print(v1);
}

稍微讲一下emplace

它与其他的插入接口有点不同:
再此之前,我们先透露一点点点vector的底层:它的模板
在这里插入图片描述

struct AA//自定义一个类
{
	int _a1 = 1;
	int _a2 = 1;

	AA(int a1 = 1, int a2 = 1)
		:_a1(a1)
		,_a2(a2)
	{}
};
void test_vector4()
{
	AA aa1 = { 0, 0 };
	vector<AA> v = { aa1, {1,1}, {2,2}, {3,3} };//看上边的模板就能看懂啦
	auto it = v.begin();
	while (it != v.end())
	{
		cout << it->_a1 << ":" << it->_a2 << endl;//注意,这里有大坑!!!!!!不可以使用解引用,AA类型不支持流输入输出!!!!!!
		++it;
	}
	cout << endl;
	v.push_back(aa1);
	v.emplace_back(aa1);

	// , Ƽ
	v.emplace_back(1,1);//它与push_back的最大区别在于,push_back的参数必须是已经实例化的对象,但是它可以直接通过参数来构造对象,然后进行插入
	v.push_back({2,2});//它是通过创建一个initializer_list对象,然后进行多参数构造的隐式类型转换生成一个AA类对象之后,进行尾插

	it = v.begin();
	while (it != v.end())
	{
		cout << it->_a1 << ":" << it->_a2 << endl;
		++it;
	}
	cout << endl;
}

摘要:

本文详解C++的vector容器:本质是顺序表即数组,核心讲接口使用与扩容等关键知识。构造方式含默认、填充、区间、拷贝及initializer_list(支持{ }直接初始化),附打印函数与调试示例。扩容用reserve(仅扩容)和resize(扩容+初始化),自动扩容通常1.5倍,手动扩容更高效。还涉及元素访问、插入删除,对比emplace_back与push_back差异,后者需实例化对象,前者可直接传参构造。

更多推荐