跟我从零开始学STL(STL代码基础02)---vector容器

引言

  亲爱的小伙伴们,欢迎回到我们的STL学习之旅!在上一篇文章中,我们一起探讨了string容器,了解了如何在C++中高效地处理字符串。今天,我们将要进入一个新的篇章,一起来学习STL中的另一个强大容器——vector。vector,即向量容器,是一种可以动态调整大小的数组,它为我们提供了比普通数组更灵活、更便捷的操作方式。无论是存储基本数据类型还是自定义对象,vector都能轻松应对。那么,让我们系好安全带,开始探索vector容器的奇妙世界吧!冲冲冲!!!

STL介绍

  STL(standard template library),叫做标准模板库,可以帮我们建立一套数据结构和算法的标准,提高复用性。STL中大量使用了模板技术。

STL中有三大部分:
容器:container,存放数据的地方,STL中实现了很多种数据结构的容器。
算法:algorithm,操作数据的方法,解决问题的方法。
迭代器:iterator,迭代器就是操作容器中数据的指针,它是对原始指针的封装,本质上是一个类模板,同时重载了指针的各种运算符。

vector容器

1.vector的基本概念

vector和数组这种结构很相似,但有区别:

  • 1.vector是一个类模板,是对数组的进一步封装。
  • 2.vector具有动态拓展的能力,而传统数组是静态的,长度不可变的。
  • vector的拓展原理:
    当空间不足的时候,vector会重新申请一块更大的空间,然后将旧空间的元素全部拷贝到新空间,再把旧空间释放掉。
  • vector涉及到两个概念:
    容量:capacity,最多可以容纳元素的个数
    大小:size,实际存放元素的个数

2.vector构造函数

  • vector v;//采用类模板实现,无参构造,构造了一个空的vector容器,就是v
  • vector vec(iterator begin,iterator end);//参数是两个迭代器,两个位置,将另一个vector的[begin,end)区间中的元素拷贝给当前vec对象,完成构造。
  • vector v(const vector& vec);//拷贝构造
  • vector v(n,ele);//ele是element,即元素。使用n个ele元素完成当前vector对象的构造。
  • 代码展示:
void test01()
{
	vector<int> v1;//无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	vector<int> v2(v1.begin(), v1.end());//v2根据v1的[begin,end)区间的值完成构造
	vector<int> v3(v2);//根据v2拷贝构造v3
	vector<int> v4(10, 100);//n个ele来构造
	//printVector()是一个遍历打印的函数
	printVector(v1);
	printVector(v2);
	printVector(v3);
	printVector(v4);
}

3.vector赋值

  • 重载赋值运算符:
      vector& operator=(const vector& vec);
  • 成员函数assign:
      assign(beg,end);//将vector[beg,end)区间的元素赋值给当前的vector对象,beg和end是两个迭代器位置
      assign(n,ele);//将n个ele元素赋值给当前vector
    • 代码展示:
//赋值
void test02()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	//使用=赋值
	vector<int> v2;
	v2 = v1;
	printVector(v2);
	//赋值函数assign
	vector<int> v3;
	v3.assign(v1.begin() + 2, v1.end() - 2);
	printVector(v3);
	vector<int> v4;
	v4.assign(10, 100);
	printVector(v4);
}

4.vector容量和大小

  • empty();//判断容器是否为空
  • capacity();//返回容器的容量
  • size();//返回容器中元素的个数
  • resize(int num);//重新指定容器的size为num,如果容器的size变多了,会以默认值填充多余的位置,如果size变少了,会删除多余的元素。
  • resize(int num,ele);//重新指定容器的size为num,如果容器的size变多了,会以ele元素填充多余的位置,如果size变少了,会删除多余的元素。
  • resize不会改变容量的大小,某些情况下就会存在空间的浪费,这时候可以通过一个缩容的函数来缩小容量shrink_to_fit()
  • 代码展示:
//容量和大小
void test03()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	cout << v1.empty() << endl;//0
	cout << "容量:" << v1.capacity() << endl;//13
	cout << "大小:" << v1.size() << endl;//10

	//resize重新指定大小
	v1.resize(5);
	printVector(v1);
	v1.resize(10);
	printVector(v1);
	v1.resize(15, 100);
	printVector(v1);

	//缩容
	cout << "v1的容量:" << v1.capacity() << endl;
	v1.resize(5);
	cout << "v1的容量:" << v1.capacity() << endl;//可见resize没有改变容量的值,此时存在空间的浪费
	v1.shrink_to_fit();
	cout << "v1的容量:" << v1.capacity() << endl;//缩容之后,容量变成5,跟size值相等

}

5.vector插入和删除

  • 尾部操作:
       push_back(ele);//尾部推入一个元素ele
       pop_back();//尾部最后一个元素ele被弹出

  • 指定位置插入:insert
       insert(iterator pos,ele);//迭代器指向位置pos插入元素ele,并且返回新插入元素的位置迭代器。
       insert(iterator pos,int n,ele);//迭代器指向位置pos插入n个元素ele,并且返回新插入多个元素的第一个元素的位置迭代器。
      insert(iterator pos,beg,end); //在pos位置插入另一个vector[beg,end)区间的数据,返回新数据的位置。

  • 删除:erase
      erase(iterator pos);//删除迭代器所指向的元素,返回被删除元素的下一个迭代器位置。
       erase(iterator beg,iterator end);//删除beg到end之间的元素,不包括end
       clear();//清除容器中的所有元素

  • 关于迭代器失效的问题:
       vector是属于序列式容器,底层是数组,是物理上连续的内存空间。所以,当发生插入或删除操作后会出现元素的移动,这样会导致
      迭代器失效,当迭代器失效后,如果继续操作迭代器,就会报错。所以为了避免错误,我们应该在迭代器失效后马上对迭代器进行更新操作,用一个新的有效的迭代器替代原来失效的迭代器。
       进一步分析失效的情况:
    1.插入操作:insert和push_back
      insert:
      当容量足够时,没有发生动态拓展时,insert插入操作会导致插入位置后面所有元素的位置发生变化,从而导致迭代器失效
      当容量不够时,会发生动态拓展,会导致所有元素的位置发生变化,导致所有迭代器失效。
      push_back:
      当容量足够时,没有发生动态拓展时,push_back会导致end迭代器失效
      当容量不够时,会发生动态拓展,会导致所有元素的位置发生变化,导致所有迭代器失效。
      2.删除操作:erase或pop_back
      erase:
      删除操作不会导致容量不够的问题,但是会导致被删除的当前位置迭代器失效,以及后面所有的迭代器失效。
      pop_back:
      只会导致end迭代器失效,但不会影响前面的迭代器。
      3.insert和erase方法都会返回迭代器,我们只需要用返回的有效迭代更新失效的迭代器即可:
    如:it=v.insert(it);
    如:it=v.erase(it);

void test04()
{
	//尾部操作
	vector<int> v1;
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	v1.push_back(50);
	v1.pop_back();//50
	v1.pop_back();//40
	printVector(v1);

	//insert插入
	v1.insert(v1.begin(), 100);
	printVector(v1);
	v1.insert(v1.begin(), 2, 1000);
	printVector(v1);
	vector<int> v2 = { 6,7,8,9,0 };
	v1.insert(v1.begin(), v2.begin(), v2.begin() + 2);
	printVector(v1);

	//删除erase
	v2.erase(v2.end() - 1);
	printVector(v2);
	v2.erase(v2.begin() + 1, v2.begin() + 3);
	printVector(v2);
	v2.clear();
	cout << v2.size() << endl;//0

	//插入和删除的返回值
	cout << "插入和删除的返回值:" << endl;
	vector<int> v3 = { 1,2,3,4,5,6 };
	vector<int>::iterator it_insert = v3.insert(v3.begin() + 1, 100);
	cout << *it_insert << endl;//100
	it_insert = v3.insert(v3.begin() + 1, 3, 1000);
	cout << *it_insert << endl;//第一个1000

	vector<int>::iterator it_erase = v3.erase(v3.begin() + 4);//删除了100
	cout << *it_erase << endl;//返回100后面的迭代器,即2的迭代器
	it_erase = v3.erase(v3.begin() + 1, v3.begin() + 4);//删除了三个1000
	cout << *it_erase << endl;//2
	printVector(v3);

}

6.vector数据存取

  • operator[int index];//重载了[]运算符,可以通过下标取值
  • at(int index);//返回索引index位置的元素
  • front();//返回容器中第一个元素
  • back();//返回容器中最后一个元素
  • 代码展示:
//迭代器失效问题
void test05()
{
	vector<int> v;
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(30);
	v.push_back(40);
	v.push_back(50);
	printVector(v);

	要求删除所有30的元素
	遍历整个数组,如果发现是30,就删掉
	//for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	//{
	//	if (*it==30)
	//	{
	//		v.erase(it);//这里会报错,原因是,第一个30被删掉了,它的迭代器就失效了,但此时的循环还继续以这个失效的it进行++,所以会报错
	//	}
	//}
	以上问题,需要在循环中,更新迭代器
	//for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	//{
	//	if (*it == 30)
	//	{
	//		it=v.erase(it);//这里更新迭代器,才可以继续下去
	//	}
	//}
	//printVector(v);
	//以上代码也有bug,会跳过第二个30
	//应该改成如下:
	//for (vector<int>::iterator it = v.begin(); it != v.end();)
	//{
	//	if (*it == 30)
	//	{
	//		it = v.erase(it);//此处更新it,已经指向被删除元素的下一个位置,相当于已经++了
	//	}
	//	else//只有不是30的时候,由于没有更新迭代器,才需要it++
	//	{
	//		it++;
	//	}
	//}
	//printVector(v);

	//插入操作也会导致迭代器失效
	//在30之前插入100
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		if (*it == 30)
		{
			it = v.insert(it, 100);
			it++;
		}
	}
	printVector(v);
}

//数据存取
void test06()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	//另外一种获取元素的方式,通过下标取值
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << "首元素:" << v1.front() << endl;
	cout << "尾元素:" << v1.back() << endl;

}

7.vector互换

  • 可以实现两个vector容器元素的互换
  • v1.swap(v2);//v1和v2互换元素
  • 代码展示:
//互换
void test07()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	vector<int> v2 = { 6,6,6,6,6,6 };
	v1.swap(v2);
	printVector(v1);
	printVector(v2);
}

8.vector预留空间

  当vector存储的数据量很大时,由于vector的动态数组特性,会导致多次的动态扩展发生,这样会降低效率。
  为了避免多次拓展的情况发生,可以在一开始就指定一些预留的空间,其实就是指定了capacity的值。
  reserve(int len);//给容器预留len个元素的位置,预留的位置不可访问,因为没有被初始化。

9.算法

  这是算法,适用于大部分容器的,不是vector的成员函数
排序:
   sort(iterator beg,iterator end);//对区间内的元素进行排序,默认升序,从小到大
  sort(iterator beg,iterator end,func);//对区间内的元素进行排序,使用func指定排序方式
反转:
  reverse(iterator beg,iterator end);//对区间内元素反转倒置
复制:
  copy(源容器起始位置,源容器结束位置,目标容器的起始位置);//将源容器的区间元素复制到目标容器指定位置,注意是替换,新复制进去的值替换了原来位置的值
查找:
  find(iterator beg,iterator end,ele);//在区间内查找元素ele,找到返回所在位置的迭代器,找不到返回end迭代器,注意这个end指的是区间中指定的end位置,不一定是整个容器的end()。
遍历:
  for_each(iterator beg,iterator end,func);//遍历容器区间内的元素,func是个函数或者仿函数,指明遍历时对元素的操作。

  • 代码展示:
//常用算法
//定义排序器函数,实现降序
bool cap(int a, int b)
{
	return a > b;
}
//定义用于for_each算法操作的函数
void myPrint(int val)
{
	val++;
	cout << val << " ";
}
void test09()
{
	//排序sort,默认升序
	vector<int> v1 = { 28,42,5,31,65,32,55,83,12,19 };
	sort(v1.begin(), v1.end());
	printVector(v1);
	//通过func函数实现降序排序,这个func函数叫做排序器函数,是sort的第三个参数
	sort(v1.begin(), v1.end(), cap);
	printVector(v1);

	//反转
	reverse(v1.begin(), v1.begin() + 5);
	printVector(v1);

	//复制
	vector<int> v2;
	v2.assign(10, 100);
	//将v1的前五个值,复制到v2的前五个位置
	copy(v1.begin(), v1.begin() + 5, v2.begin());
	printVector(v2);

	//查找
	vector<int>::iterator it = find(v1.begin(), v1.begin() + 5, 83);
	if (it == v1.begin() + 5)
	{
		cout << "没找到" << endl;
	}
	else
	{
		cout << "找到了,它的值是:" << *it << ",它的下标是:" << it - v1.begin() << endl;
	}

	//遍历
	for_each(v1.begin(), v1.end(), myPrint);
}

10.总结

  vector是单端数组,是单端开口的,这种数据结构,决定了它在尾部的插入和删除操作效率是比较高的,因为不会导致其他元素的移动。
  但是,在其他位置插入或者删除的时候,效率较低,因为会导致其他元素移动。
vector由于是连续的物理空间存储的结构,底层是数组,所以可以使用下标对元素进行访问,但比数组好用,多了动态拓展。
  这种结构,做遍历查询的时候,效率很高。

11.小练习

  • 1.请大家使用三种不同的方式对一个vector进行遍历打印操作
void test10()
{
	vector<int> v = { 1,2,3,4,5,6 };
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	//while循环或者for_each算法
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
}
  • 2.使用vector容器存储多个Person(m_Name,m_Age)对象,然后输出每个对象的属性
class Person
{
	string m_Name;
	int m_Age;
public:
	Person(string name, int age)
	{
		m_Name = name;
		m_Age = age;
	}
	void show() { cout << "name:" << m_Name << ",age:" << m_Age << endl; }
};
void test11()
{
	vector<Person> v;
	Person p1("tom", 10);
	Person p2("lucy", 20);
	Person p3("john", 14);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	//v = { p1,p2,p3 };
	for (int i = 0; i < v.size(); i++)
	{
		v[i].show();
	}
}
  • 3.实现一个嵌套型的vector,一个vector中存放的元素还是vector,具体来说是vector,请输出每个vector中的int元素
void test12()
{
	vector<vector<int>> v;
	vector<int> v1 = { 1,2,3 };
	vector<int> v2 = { 4,5,6 };
	vector<int> v3 = { 7,8,9 };
	v = { v1,v2,v3 };
	//二维数组,需要两层循环
	for (int i = 0; i < v.size(); i++)
	{
		for (int j = 0; j < v[i].size(); j++)
		{
			cout << v[i][j] << " ";
		}
		cout << endl;
	}
	for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++)
	{
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
		{
			cout << *vit << " ";
		}
		cout << endl;
	}

}

结语

  通过本篇文章的学习,我们共同掌握了vector容器的基本用法,包括它的构造、添加、删除、访问元素等功能。vector的灵活性和便捷性使其成为C++程序设计中不可或缺的工具之一。

  当然,vector还有更多高级特性等待我们去挖掘,比如迭代器、算法操作等。在接下来的学习中,我们将继续深入探讨这些内容,让大家能够更加熟练地运用vector解决实际问题。如果你在学习和使用vector的过程中有任何疑问,欢迎留言讨论。让我们一起在C++的编程世界中不断进步,下期再见!

  都学到这里啦!给厉害的自己鼓鼓掌吧!!!👏👏👏👏👏👏👏👏

  • 32
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值