文章

vector常用操作

1.前言

昨晚在家,心血来潮想了解下vector,所以翻cppreference看了看,今天便小小总结下

2.常用函数的思维导图

这是我把cppreference中我常用的摘录下来做成的思维导图,更清晰一点

微信截图_20240305160254.png

2.1迭代器

迭代器是什么? 简单理解:提供遍历访问的一种方式 官方理解:是一个对象,可以循环访问C++标准库容器中的元素,并提供对各个元素的访问

cbegin的c代表的是返回const,所以他不能修改数据 rbegin的r代表反向第一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int main()
{
	std::vector<int> v = { 0,1,2,3,4,5 };
	for (auto it = v.begin(); it < v.end(); it++)
	{
		std::cout << *it << " ";
	}
	std::cout << std::endl;
	for (auto it = v.rbegin(); it < v.rend(); it++)
	{
		std::cout << *it << " ";
	}
	std::cout << std::endl;
	for (auto it = v.cbegin(); it < v.cend(); it++)
	{
		std::cout << *it << " ";
		// *it = 1;  错误
	}
	std::cout << std::endl;
	for (auto it = v.crbegin(); it < v.crend(); it++)
	{
		std::cout << *it << " ";
		// *it = 1;  错误
	}
}

输出

1
2
3
4
0 1 2 3 4 5
5 4 3 2 1 0
0 1 2 3 4 5
5 4 3 2 1 0

2.2容量

size:现在容器中有多少个元素

resize:当新分的内存比现在内存大时,后面新扩充的内存部分,都会用默认元素来填充,所以capacity(内存)也变大;没现在内存大时,原来内存中的元素多的元素会被删除,但是capacity不变

capacity:你分配了多少的内存,即使你里面元素减少了,但是内存也不会减少

shrink_to_fit:把多余的内存给删掉,比如元素4个,内存有5,则把多余的一个内存给干掉,注意这个函数没有返回值

reserve:是内存预留空间,但是没有给新开的内存初始化,只是说明可以利用它,但是不能有效访问空间,因为现在里面什么都没,所以元素个数其实没变。如果预留空间大于当前capacity空间则会重新分配内存 reserve:并不会删除或者添加元素,也不会改变元素的数量 reserve:这个函数设计的是只能用来增加capacity,不能用来减少capacity,传入的参数小于或等于当前的capacity,reserve函数不会做任何事情,所以capacity保持不变

v[5]:这样通过索引的只能访问已存在的元素,否则就访问越界

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
	std::vector<int> v;
	std::cout << "size:" << v.size() << std::endl;
	// 0
	
	v = { 1,2,3,4,5 };
	std::cout << "size:" << v.size() << std::endl;
	// 5

	v = { 1,2,3,4 };
	std::cout << "size:" << v.size() << std::endl;
	// 4 元素为4个

	std::cout << "capacity:" << v.capacity() << std::endl;
	// 5 内存为5个空间大小

	v.shrink_to_fit();
	std::cout << "capacity:" << v.capacity() << std::endl;
	// 4

	v.resize(10);
	std::cout << "capacity:" << v.capacity() << std::endl;
	// 10
	std::cout << "v[5]:" << v[5] << std::endl;
	// 0 因为resize新增空间时,用默认数据来填充新增加的空间

	for (auto it = v.begin(); it < v.end(); it++)
	{
		std::cout << *it << " ";
	}
	std::cout << std::endl;
	// 1 2 3 4 0 0 0 0 0 0

	v.resize(3);
	for (auto it = v.begin(); it < v.end(); it++)
	{
		std::cout << *it << " ";
	}
	std::cout << std::endl;
	// 1 2 3

	std::cout << "capacity:" << v.capacity() << std::endl;
	// 10

	v.shrink_to_fit();
	std::cout << "capacity:" << v.capacity() << std::endl;
	// 3

	v.reserve(9);
	for (auto it = v.begin(); it < v.end(); it++)
	{
		std::cout << *it << " ";
	}
	std::cout << std::endl;
	// 1 2 3 
	std::cout << "capacity:" << v.capacity() << std::endl;
	// 9
	std::cout << "v[5]:" << v[5] << std::endl;
	// 6619252 随机地址,因为reserve分配的空间还没有初始化,但是resize分配的时候也就初始化了,所以上面的v[5]是0
	// 但其实这样访问是错误的v[5]这是通过[]操作符索引来访问的,但是索引访问只能访问已存在的元素,而此时已存在的元素只有1 2 3
	// 这就是访问越界,因为v里面只有3个元素,5超过了有效范围

	v.reserve(2);
	for (auto it = v.begin(); it < v.end(); it++)
	{
		std::cout << *it << " ";
	}
	std::cout << std::endl;
	// 1 2 3
	std::cout << "capacity:" << v.capacity() << std::endl;
	// 9 因为reserve(2) 2小于9,所以什么都不做

2.3元素访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
		std::vector<int> v = { 0,1,2,3,4,5,6 };

		v.resize(3);
		std::cout << "capacity:" << v.capacity() << std::endl;
		// 7
	
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// 0 1 2

		std::cout << "v[6]:" << v[6] << std::endl;
		// 6

		//std::cout << v.at(3) << std::endl;
		// 代码会报错,因为at里面会检查是否访问的超过了size,也就是有效元素的范围
		// 而operator[]则没有这个检查

		std::cout << "v.at(2):" << v.at(2) << std::endl;
		// 2

		std::cout << "v.front():" << v.front() << std::endl;
		// 0

		std::cout << "v.back():" << v.back() << std::endl;
		// 2

resize(3)执行后,应该元素里面就剩下0,1,2了,为什么v[6]还能访问呢

这和resize和迭代器工作方式有关,当调用v.resize(3);后,v的大小(size)变为3,但是capacity可能仍然保持不变。这意味着,虽然你删除了一些元素,但是这些元素占用的内存并没有被释放。因此,当你访问v[6]时,你可能仍然能够看到原来的值,因为那块内存尚未被覆盖。然而,这是非常危险的,因为这是未定义的行为。

另一方面,当你使用迭代器遍历v时,迭代器只会访问v的有效元素,也就是说,只会访问v的大小(size)范围内的元素。因此,当你执行for (auto it = v.begin(); it < v.end(); it++)时,你只会看到v的前三个元素,即0,1,2,而不会看到6,因为6已经不再v的大小范围内了。

所以,[]操作符和*it都是读取内存的值,但是他们访问的范围是不同的。[]操作符可以访问到任何位置的内存,包括超出v的大小范围的内存,而*it只能访问到v的大小范围内的内存。

2.4修改数据

assign会清除容器里以前的内容,当输入的元素比原来的容量空间小时,则原来容量空间不变 emplace替代insert emplace_back替代push_back

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
		std::vector<int> v = { 0,1,2,3,4,5,6 };

		v.assign(4, 3);
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		// 3 3 3 3
		std::cout << std::endl;
		std::cout << "v.capacity():" << v.capacity() << std::endl;
		// 7

		v.pop_back();
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// 3 3 3

		v = { 0,1,2,3,4,5,6 };
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// 0 1 2 3 4 5 6
		
		v.insert(v.begin(), -1);
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// -1 0 1 2 3 4 5 6

		v.insert(v.begin() + 3, 2, 999);
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// -1 0 1 999 999 2 3 4 5 6

		std::vector<int> v1 = { 11,11,11 };
		v.insert(v.begin(), v1.begin(), v1.end());
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// 11 11 11 -1 0 1 999 999 2 3 4 5 6

		v.erase(v.begin());
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// 11 11 -1 0 1 999 999 2 3 4 5 6

		// 左开右闭
		v.erase(v.begin(), v.begin() + 2);
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// -1 0 1 999 999 2 3 4 5 6

		v.erase(v.begin() + 3, v.begin() + 5);
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// -1 0 1 2 3 4 5 6

		v.clear();
		std::cout << "capacity:" << v.capacity() << std::endl;
		//v.at(0); 会报错

		v = { 1,2,3,4,5 };
		v1 = { 6,7 };
		std::cout << "v1:";
		for (auto it = v1.begin(); it < v1.end(); it++)
		{
			std::cout <<  * it << " ";
		}
		// 6 7
		std::cout << "v1.capacity:" << v1.capacity() << std::endl;
		// 3 因为之前v1是3个元素,现在是2个元素

		v.swap(v1);
		std::cout << "v.swap after:";
		for (auto it = v.begin(); it < v.end(); it++)
		{
			std::cout << *it << " ";
		}
		// 6 7 
		std::cout << "v.swap after's capacity:" << v.capacity() << std::endl;
		// 3  因为v1内存空间是3
		std::cout << std::endl;

		for (auto it = v1.begin(); it < v1.end(); it++)
		{
			std::cout << *it << " ";
		}
		// 1 2 3 4 5
		std::cout << "v1.capacity:" << v1.capacity() << std::endl;
		// 15
		std::cout << std::endl;

		v1.emplace(v1.begin(), 0);
		for (auto it = v1.begin(); it < v1.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
		// 0 1 2 3 4 5

		v1.emplace_back(6);
		for (auto it = v1.begin(); it < v1.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;

3.迭代器失效的情况

本文由作者按照 CC BY 4.0 进行授权