【C++第十一章】Vector

【C++第11章】Vector

vector介绍🧐

  vector是表示可变大小数组的序列容器,它类似于数组,但大小可以动态改变,并且大小会被容器自动处理。本质上说,vector使用动态分配数组来存储元素,为了减少扩容代价,vector会分配额外空间进行元素存储。

  对比其他动态序列容器,vector访问元素时更加高效,尾插和尾删相对更容易,可以将其看为一个顺序表进行理解。

vector基本使用🧐

初始化和遍历🔎

  由于C++封装的特性,所以vector与string的使用相似,首先来看初始化和遍历:

```c++
	vector<int> v(10,0);

	vector<int> v2(10, 99);

	vector<int> arr = { 100,9,10,8,6,5,4,3,2,1 }; //数组初始化

	vector<int> v3(v2.begin(), v2.end());

	string s("hello world");

	vector<int> v4(s.begin(), s.end());

	for (size_t i = 0; i < v2.size(); i++) //下标遍历
	{
		cout << v2[i] << " ";
	}
	cout << endl;

	vector<int>::iterator it = v4.begin(); //迭代器遍历
	while (v4.end() != it)
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	for (auto s : v) //范围for
	{
		cout << s << " ";
	}
```

Pasted image 20240727095533

扩容🔎

  在vs下扩容约1.5倍,g++下为2倍

Pasted image 20240727095601

vector常用函数🧐

reserve和resize🔎

  reserve不会对开出的空间初始化,所以仅capacity变动,size还是原来的,如果我们强行访问就会触发下标断言。

  resize可以自动初始化,且size会跟随数据量进行变动。

Pasted image 20240727100516

shrink_to_fit🔎

  当size小于capacity时,shrink可以进行缩容,但一般是异地缩容,是用时间来换空间,要小心使用。

Pasted image 20240727100855

insert、erase、front和back🔎

void test_vector4()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	cout << v.front() << endl; //取头
	v.insert(v.begin(), 100); //头插100
	cout << v.front() << endl;
	cout << v.back() << endl; //取尾
	v.erase(v.begin()); //头删 
	for (auto s : v)
	{
		cout << s << " ";
	}
}

Pasted image 20240727102058

find🔎

  vector没有自己的find函数,但STL库中有find,而string由于要找字符串,STL的find不能满足需求,所以string有自己的find。

Pasted image 20240727102327

clear🔎

  clear一般情况不清理capacity,不过可以配合shrink将capacity也清理掉

Pasted image 20240727102759

流插入流提取🧐

  vector不提供流插入和流提取,因为编译器不知道你要以什么形式打印出来,并且已有的函数足够使用,所以流插入和流提取没有必要

image-20240731200745690

迭代器失效🧐

  迭代器失效是指对容器的操作影响了元素的存放位置,导致迭代器指向的空间被销毁,而使用一块已经被释放的空间,造成的后果是程序崩溃

  如下代码,当我们使用erase删除偶数时,碰到连续的偶数或者末尾是偶数就会出现问题。

void test_vector1()
{
	vector<int> v;
	v.push_back(2); //连续的偶数删除不完整
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
    //v.push_back(6); //末尾为偶数会直接报错
	auto it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			v.erase(it);
		}
		++it;
	}
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

Pasted image 20240729225332

Pasted image 20240729225228

  原因在于,当我们erase完数据后,数据会挪动位置,而迭代器也跟着挪动,此时如果下一位是奇数那恰好不删除,如果是偶数就会跳过数据

动画

  当末尾为偶数时,erase后迭代器还是会继续往后走,it超过了end,此时已经是非法访问了,++it将会使程序崩溃。

动画1

  并且在insert后由于扩容会删除旧空间,而迭代器还指向原空间,也会出现迭代器失效。所以我们默认为会引起其底层空间改变的操作,都有可能使迭代器失效,在vs19下STL会强制检查,进行扩容删除操作后再使用迭代器会报错,g++下不会强制检查。

  解决方式很简单,erase和insert都给了返回值,我们只需要用迭代器接收新的地址即可。

Pasted image 20240729231123

memcpy的拷贝问题🧐

  在模拟底层实现时发现,写扩容用memcpy会出现一个奇怪的问题,当我们使用vector存string类型时,前四次尾插都正常。

Pasted image 20240729231652

  但是到了第五次,vector扩容,此时打印出错了。

Pasted image 20240729231740

  在了解后发现,memcpy是内存的二进制格式拷贝,将一段空间内容原封不动拷贝到另一段内存空间中,如果是内置类型,memcpy可以高效正确的拷贝,但自定义类型使用memcpy,且涉及空间时就会出错,因为memcpy实际是浅拷贝,memcpy会把_str的指针指向旧空间,当我们delete旧空间后,析构导致_str变成随机值,所以打印出错。

image-20240802191357383

  使用for循环一个一个赋值就可以解决了。

Pasted image 20240729235733

结尾👍

以上便是vector的全部介绍,如果有疑问或者建议都可以私信笔者交流,大家互相学习,互相进步!🌹

  • 34
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A.A呐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值