为什么要学习vector?什么是vector?
vector
是标准模板库(STL
)提供的一个容器类,它是动态数组的一种实现。这意味着它可以像普通数组一样存储一组相同类型的元素,并且能根据需要自动调整自身的大小,例如,你可以创建一个存储整数的 vector
,然后不断往里面添加或删除元素,它会自动管理内存空间
vector
类是和 STL
库一起问世的,string
函数是在 STL
库之前创造的,为了一致性简便性,vector
、list
等类都减少了一部分不必要的函数,也将 string
加入了 STL
库

vector 的主要特征可总结为:
vector
是表示可变大小数组的序列容器。
- 就像数组一样,
vector
也采用的连续存储空间来存储元素。也就是意味着可以采用下标对 vector
的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理
- 本质讲,
vector
使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector
并不会每次都重新分配大小
vector
分配空间策略:vector
会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的
- 因此,
vector
占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长
- 与其它动态序列容器相比(
deque
,list and forward_list
),vector
在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起 list
和 forward_list
统一的迭代器和引用更好
vector类对象的常见构造

vector
作为一个类也有构造函数,析构函数,=运算符重载,我们重点介绍构造函数里的功能
函数名 |
功能说明 |
vector() |
无参构造 |
vector(size_type n, const value_type& val = value_type()) |
构造并初始化n个val |
vector (const vector& x) |
拷贝构造 |
vector (InputIterator first, InputIterator last) |
使用迭代器进行初始化构造 |
💻代码测试示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> #include <vector> using namespace std;
int main() { vector<int> first; vector<int> second(4, 100); vector<int> third(second.begin(), second.end()); vector<int> fourth(third);
int myints[] = { 16,2,77,29 }; vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int));
cout << "The contents of fifth are:"; for (vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it) cout << ' ' << *it; cout << '\n';
return 0; }
|
⌨️代码输出示例:

vector类对象的容量操作

在 vector
中同样对数组实现了容量操作,只不过相比 string
,删掉了 lenth
这类作用不大的函数
函数名 |
功能说明 |
size |
返回数组有效数据个数 |
max_size |
返回的是 vector 理论上能够容纳的最大有效数据 |
resize |
将有效数据的个数增加或减少 n 个,多出的空间用默认值,少的截断即可 |
capacity |
返回空间总大小,即容量 |
reserve |
为数组增加预留空间,即增加预留容量 |
empty |
检测数组是否为空,是返回 true ,否则返回 false |
shrink_to_fit |
请求 vector 对象将其容量缩小到和当前有效数据个数相匹配的大小 |
🔥值得注意的是:
capacity
的代码在 vs
和 g++
下分别运行会发现,vs
下 capacity
是按 1.5
倍增长的,g++
是按 2
倍增长的。这个问题经常会考察,不要固化的认为,vector
增容都是 2
倍,具体增长多少是根据具体的需求定义的,vs
是 PJ
版本 STL
,g++
是 SGI
版本 STL
reserve
只负责开辟空间,如果确定知道需要用多少空间,reserve
可以缓解 vector
增容的代价缺陷问题
resize
在开空间的同时还会进行初始化,影响 size
💻代码测试示例:
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
| #include <iostream> #include <vector> using namespace std;
int main() { vector<int> v(10, 1); cout << "size:" << v.size() << endl; cout << "max_size:" << v.max_size() << endl;
v.resize(15); cout << "resize:" << v.size() << ' ' << "v:"; for (size_t i = 0; i < v.size(); ++i) { cout << v[i] << ' '; } cout << endl;
v.reserve(20); cout << "reserve:" << v.capacity() << endl; cout << "capacity:" << v.capacity() << endl; cout << "empty:" << v.empty() << endl;
v.shrink_to_fit(); cout << "shrink_to_fit:" << v.capacity() << endl;
return 0; }
|
⌨️代码输出示例:

vector类对象的迭代器

vector
的迭代器和 string
的基本使用方法一致
函数名 |
功能说明 |
begin + end |
迭代器:begin 获取开头一个数据 + end 获取最后一个数据下一个位置 |
rbegin + rend |
反向迭代器:rbegin 获取最后一个数据 + end 获取开头一个数据上一个位置 |
cbegin + cend |
和 begin + end 一样,但是常量迭代器只读 |
crbegin + crend |
和 rbegin + rend 一样,但是反向常量迭代器只读 |
💻代码测试示例:
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
| #include <iostream> #include <vector> using namespace std;
int main() { vector<int> v(10); for (size_t i = 0; i < 10; ++i) { v[i] = i; }
cout << "迭代器:"; vector<int>::iterator it1 = v.begin(); while (it1 != v.end()) { cout << *it1 << ' '; it1++; } cout << endl;
cout << "反向迭代器:"; vector<int>::reverse_iterator it2 = v.rbegin(); while (it2 != v.rend()) { cout << *it2 << ' '; it2++; } cout << endl;
return 0; }
|
⌨️代码输出示例:

vector类对象的元素修改

vector
相对于 string
增加了 emplace
,去除了多余的 append
函数名 |
功能说明 |
assign |
将新的内容赋值给数组 |
push_back |
数组尾插有效数据 |
pop_back |
数组尾删有效数据 |
insert |
在容器的指定位置插入元素 |
erase |
从容器里移除指定的元素或元素范围 |
swap |
交换两个 vector 对象的内容 |
clear |
移除 vector 对象中存储的所有字符 |
emplace |
在容器的指定位置直接构造元素 |
emplace_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
| #include <iostream> #include <vector> using namespace std;
int main() { vector<int> v{ 0,1,2,3,4,5,6,7,8,9 };
cout << "v:"; vector<int>::iterator it1 = v.begin(); while (it1 != v.end()) { cout << *it1 << ' '; it1++; } cout << endl;
v.assign(10, 0); cout << "assign:"; vector<int>::iterator it2 = v.begin(); while (it2 != v.end()) { cout << *it2 << ' '; it2++; } cout << endl; v.push_back(5); cout << "push_back:"; vector<int>::iterator it3 = v.begin(); while (it3 != v.end()) { cout << *it3 << ' '; it3++; } cout << endl;
v.pop_back(); cout << "pop_back:"; vector<int>::iterator it4 = v.begin(); while (it4 != v.end()) { cout << *it4 << ' '; it4++; } cout << endl;
v.insert(v.begin() + 2, 3, 1); cout << "insert:"; vector<int>::iterator it5 = v.begin(); while (it5 != v.end()) { cout << *it5 << ' '; it5++; } cout << endl;
v.erase(v.begin() + 2, v.begin() + 5); cout << "erase:"; vector<int>::iterator it6 = v.begin(); while (it6 != v.end()) { cout << *it6 << ' '; it6++; } cout << endl;
vector<int> vv{ 1,3,4,5,6,7,8,9 }; cout << "vv:"; vector<int>::iterator it7 = vv.begin(); while (it7 != vv.end()) { cout << *it7 << ' '; it7++; } cout << endl;
swap(v, vv); cout << "swap:" << ' ' << "v:"; vector<int>::iterator it8 = v.begin(); while (it8 != v.end()) { cout << *it8 << ' '; it8++; } cout << ' ' << "vv:"; vector<int>::iterator it9 = vv.begin(); while (it9 != vv.end()) { cout << *it9 << ' '; it9++; } cout << endl;
v.emplace(v.begin(), 100); cout << "emplace:"; vector<int>::iterator it10 = v.begin(); while (it10 != v.end()) { cout << *it10 << ' '; it10++; } cout << endl;
v.emplace_back(100); cout << "emplace_back:"; vector<int>::iterator it11 = v.begin(); while (it11 != v.end()) { cout << *it11 << ' '; it11++; } cout << endl;
v.clear(); cout << "clear:"; vector<int>::iterator it12 = v.begin(); while (it12 != v.end()) { cout << *it12 << ' '; it12++; } cout << endl;
return 0; }
|
⌨️代码输出示例:

vector类对象的元素访问

vector
的本质是数组,固然是要[]运算符重载
函数名 |
功能说明 |
operator[ ] |
像数组一样,使用方括号语法来访问其内部数据 |
at |
访问指定位置元素 |
back |
返回容器中最后一个元素的引用 |
front |
返回容器中第一个元素的引用 |
💻代码测试示例:
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
| #include <iostream> #include <vector> using namespace std;
int main() { vector<int> v{ 0,1,2,3,4,5,6,7,8,9 };
cout << "operator[ ]:" << v[5] << endl; cout << "at:" << v.at(5) << endl; cout << "back:" << (v.back() = 10) << ' ' << "v:"; vector<int>::iterator it1 = v.begin(); while (it1 != v.end()) { cout << *it1 << ' '; ++it1; } cout << endl;
cout << "front:" << (v.front() = -1) << ' ' << "v:"; vector<int>::iterator it2 = v.begin(); while (it2 != v.end()) { cout << *it2 << ' '; ++it2; } cout << endl; return 0; }
|
⌨️代码输出示例:

vector迭代器失效问题
迭代器的主要作用就是让算法能够不用关心底层数据结构
,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector
的迭代器就是原生态指针 T*
。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)
🚩会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize
、reserve
、insert
、assign
、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
| #include <iostream> using namespace std; #include <vector> int main() { vector<int> v{1,2,3,4,5,6}; auto it = v.begin(); v.assign(100, 8);
while(it != v.end()) { cout<< *it << " " ; ++it; } cout<<endl; return 0; }
|
🚩指定位置元素的删除操作–erase
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> using namespace std; #include <vector> int main() { int a[] = { 1, 2, 3, 4 }; vector<int> v(a, a + sizeof(a) / sizeof(int)); vector<int>::iterator pos = find(v.begin(), v.end(), 3); v.erase(pos); cout << *pos << endl; return 0; }
|
erase
删除 pos
位置元素后,pos
位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果 pos
刚好是最后一个元素,删完之后 pos
刚好是 end
的位置,而 end
位置是没有元素的,那么 pos
就失效了。因此删除 vector
中任意位置上元素时,vs
就认为该位置迭代器失效了
以下代码的功能是删除vector中所有的偶数,请问那个代码是正确的,为什么?
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
| #include <iostream> using namespace std; #include <vector> int main() { vector<int> v{ 1, 2, 3, 4 }; auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) v.erase(it); ++it; }
return 0; } int main() { vector<int> v{ 1, 2, 3, 4 }; auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) it = v.erase(it); else ++it; } return 0; }
|
显然第二个是正确的
使用 v.erase(it)
删除元素后,迭代器 it
会失效,v.erase(it)
删除元素时,erase
函数会返回一个指向被删除元素之后元素的迭代器,这意味着在调用 v.erase(it)
之后,it
不再指向原来的迭代器,所以需要将这个返回值赋给 it
,可以保证 it
始终指向一个有效的元素,从而避免了迭代器失效的问题
🚩Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端
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
| int main() { vector<int> v{ 1,2,3,4,5 }; for (size_t i = 0; i < v.size(); ++i) cout << v[i] << " "; cout << endl; auto it = v.begin(); cout << "扩容之前,vector的容量为: " << v.capacity() << endl; v.reserve(100); cout << "扩容之后,vector的容量为: " << v.capacity() << endl;
while (it != v.end()) { cout << *it << " "; ++it; } cout << endl; return 0; } 程序输出: 1 2 3 4 5 扩容之前,vector的容量为: 5 扩容之后,vector的容量为 : 100 0 2 3 4 5 409 1 2 3 4 5
#include <vector> #include <algorithm> int main() { vector<int> v{ 1,2,3,4,5 }; vector<int>::iterator it = find(v.begin(), v.end(), 3); v.erase(it); cout << *it << endl; while (it != v.end()) { cout << *it << " "; ++it; } cout << endl; return 0; } 程序可以正常运行,并打印: 4 4 5
int main() { vector<int> v{ 1,2,3,4,5 }; auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) v.erase(it); ++it; } for (auto e : v) cout << e << " "; cout << endl; return 0; } ========================================================
[sly@VM - 0 - 3 - centos 20220114]$ g++ testVector.cpp - std = c++11 [sly@VM - 0 - 3 - centos 20220114]$ . / a.out 1 3 5 ======================================================== =
[sly@VM - 0 - 3 - centos 20220114]$ vim testVector.cpp [sly@VM - 0 - 3 - centos 20220114]$ g++ testVector.cpp - std = c++11 [sly@VM - 0 - 3 - centos 20220114]$ . / a.out Segmentation fault
|
从上述三个例子中可以看到:SGI STL
中,迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果 it
不在 begin
和 end
范围内,肯定会崩溃的
与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效
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
| #include <string> void TestString() { string s("hello"); auto it = s.begin(); while (it != s.end()) { cout << *it; ++it; } cout << endl; it = s.begin(); while (it != s.end()) { it = s.erase(it); ++it; } }
|
总结: 在使用新的 iterator
类型的变量前,对迭代器重新赋值即可
希望读者们多多三连支持
小编会继续更新
你们的鼓励就是我前进的动力!
