C++ 模板类vector用法详解
1. 引言
在C++编程中,vector
是一个非常强大且常用的模板类,它定义在<vector>
头文件中。vector
提供了动态数组的功能,与普通数组相比,它能自动管理内存,并且支持各种方便的操作,如插入、删除、遍历等。下面我们将详细介绍vector
的用法。
2. 定义与初始化
2.1 基本定义
要使用vector
,首先需要包含<vector>
头文件。定义一个vector
对象的基本语法如下:
#include <vector>
// 定义一个存储int类型的vector
std::vector<int> vec;
这里std::vector<int>
表示一个存储int
类型元素的vector
容器,vec
是这个容器的对象名。
2.2 初始化方式
-
默认初始化:
std::vector<int> v1; // 初始化一个空的vector
此时
v1
不包含任何元素,其大小为0。 -
使用初始值列表初始化:
std::vector<int> v2 = {1, 2, 3, 4, 5}; // C++11起支持这样的初始化方式,v2包含5个元素,分别为1, 2, 3, 4, 5
-
使用构造函数指定大小和初始值:
std::vector<int> v3(10, 5); // v3包含10个元素,每个元素的值都是5
-
通过其他容器初始化:
std::vector<int> v4(v2); // v4通过v2初始化,v4的内容与v2相同 std::vector<int> v5(v2.begin(), v2.end()); // 同样通过v2的迭代器范围初始化v5
3. 元素访问
3.1 通过下标访问
vector
支持通过下标运算符[]
来访问元素,就像普通数组一样。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::cout << "第一个元素: " << vec[0] << std::endl;
std::cout << "第三个元素: " << vec[2] << std::endl;
return 0;
}
在上述代码中,vec[0]
访问vector
的第一个元素,vec[2]
访问第三个元素。需要注意的是,使用[]
访问元素时,不会进行越界检查,如果访问越界,程序可能会崩溃或产生未定义行为。
3.2 使用at()
成员函数访问
vector
提供了at()
成员函数来访问元素,它会进行边界检查。如果访问越界,会抛出std::out_of_range
异常。示例如下:
#include <iostream>
#include <vector>
#include <exception>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
try {
std::cout << "第一个元素: " << vec.at(0) << std::endl;
std::cout << "第六个元素: " << vec.at(5) << std::endl;
} catch (const std::out_of_range& e) {
std::cerr << "捕获到越界异常: " << e.what() << std::endl;
}
return 0;
}
在这个例子中,访问vec.at(5)
会抛出异常,程序通过try - catch
块捕获并处理这个异常。
3.3 访问第一个和最后一个元素
vector
提供了front()
和back()
成员函数来分别访问第一个和最后一个元素。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::cout << "第一个元素: " << vec.front() << std::endl;
std::cout << "最后一个元素: " << vec.back() << std::endl;
return 0;
}
front()
返回第一个元素的引用,back()
返回最后一个元素的引用。
4. 容量与大小
4.1 大小(size
)
size()
成员函数返回vector
中当前元素的数量。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::cout << "vector的大小: " << vec.size() << std::endl;
return 0;
}
上述代码输出vector
中元素的个数,即5。
4.2 容量(capacity
)
capacity()
成员函数返回vector
在不重新分配内存的情况下可以容纳的元素数量。vector
的容量通常大于或等于其大小。当vector
的大小达到容量时,再添加新元素,vector
会重新分配内存,将容量扩大。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
std::cout << "初始容量: " << vec.capacity() << std::endl;
for (int i = 0; i < 10; ++i) {
vec.push_back(i);
std::cout << "添加元素 " << i << " 后,容量: " << vec.capacity() << std::endl;
}
return 0;
}
在这个例子中,我们可以观察到随着元素的添加,vector
的容量如何变化。通常,vector
在重新分配内存时,会将容量增加为原来的两倍(具体实现可能因编译器而异)。
4.3 调整大小(resize
)
resize()
成员函数用于改变vector
的大小。如果新的大小大于当前大小,会在vector
末尾添加默认值(对于内置类型为0)或指定值;如果新的大小小于当前大小,会删除超出部分的元素。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.resize(8);
// 此时vec的大小变为8,新增的3个元素值为0
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
vec.resize(3);
// 此时vec的大小变为3,删除后面5个元素
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
4.4 预留空间(reserve
)
reserve()
成员函数用于预先分配一定的容量,以避免多次重新分配内存。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
vec.reserve(100);
// 预先分配能容纳100个元素的空间
for (int i = 0; i < 50; ++i) {
vec.push_back(i);
}
std::cout << "当前容量: " << vec.capacity() << std::endl;
return 0;
}
在上述代码中,我们预先为vec
预留了100个元素的空间,这样在添加50个元素的过程中,不会发生重新分配内存的操作。
5. 元素插入与删除
5.1 尾部插入(push_back
)
push_back()
成员函数用于在vector
的尾部添加一个元素。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3};
vec.push_back(4);
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
上述代码在vec
的尾部添加了元素4,输出结果为1 2 3 4
。
5.2 任意位置插入(insert
)
insert()
成员函数可以在vector
的任意位置插入元素。其基本语法有多种形式:
-
在指定位置插入单个元素:
#include <iostream> #include <vector> int main() { std::vector<int> vec = {1, 2, 4, 5}; auto it = vec.begin() + 2; vec.insert(it, 3); for (int i : vec) { std::cout << i << " "; } std::cout << std::endl; return 0; }
在这个例子中,我们使用
insert
在vec
的第三个位置(索引为2)插入元素3。 -
在指定位置插入多个相同元素:
#include <iostream> #include <vector> int main() { std::vector<int> vec = {1, 2, 4, 5}; auto it = vec.begin() + 2; vec.insert(it, 3, 10); // 在it位置插入3个值为10的元素 for (int i : vec) { std::cout << i << " "; } std::cout << std::endl; return 0; }
-
在指定位置插入另一个容器的元素范围:
#include <iostream> #include <vector> int main() { std::vector<int> vec1 = {1, 2, 3}; std::vector<int> vec2 = {4, 5, 6}; auto it = vec1.begin() + 1; vec1.insert(it, vec2.begin(), vec2.end()); for (int i : vec1) { std::cout << i << " "; } std::cout << std::endl; return 0; }
上述代码将
vec2
的所有元素插入到vec1
的第二个位置(索引为1)。
5.3 尾部删除(pop_back
)
pop_back()
成员函数用于删除vector
的最后一个元素。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.pop_back();
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
执行pop_back()
后,vec
的最后一个元素5被删除,输出结果为1 2 3 4
。
5.4 任意位置删除(erase
)
erase()
成员函数可以删除vector
中指定位置或指定范围的元素。
-
删除单个元素:
#include <iostream> #include <vector> int main() { std::vector<int> vec = {1, 2, 3, 4, 5}; auto it = vec.begin() + 2; vec.erase(it); for (int i : vec) { std::cout << i << " "; } std::cout << std::endl; return 0; }
上述代码删除了
vec
中索引为2的元素,即3。 -
删除元素范围:
#include <iostream> #include <vector> int main() { std::vector<int> vec = {1, 2, 3, 4, 5}; auto first = vec.begin() + 1; auto last = vec.begin() + 3; vec.erase(first, last); for (int i : vec) { std::cout << i << " "; } std::cout << std::endl; return 0; }
这里删除了从索引1到索引3(不包括索引3)的元素,即2和3。
5.5 清空vector
(clear
)
clear()
成员函数用于删除vector
中的所有元素,将其大小变为0,但容量不变。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.clear();
std::cout << "vector的大小: " << vec.size() << std::endl;
std::cout << "vector的容量: " << vec.capacity() << std::endl;
return 0;
}
执行clear()
后,vec
的大小变为0,容量保持不变。
6. 迭代器
6.1 迭代器的概念
迭代器是一种类似指针的对象,用于遍历容器中的元素。vector
提供了多种迭代器类型,包括正向迭代器、反向迭代器等。
6.2 正向迭代器
begin()
成员函数返回一个指向vector
第一个元素的正向迭代器,end()
成员函数返回一个指向vector
最后一个元素之后位置的正向迭代器。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
在上述代码中,it
是一个正向迭代器,通过*it
可以访问迭代器指向的元素。++it
使迭代器指向下一个元素。
6.3 反向迭代器
rbegin()
成员函数返回一个指向vector
最后一个元素的反向迭代器,rend()
成员函数返回一个指向vector
第一个元素之前位置的反向迭代器。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
这段代码使用反向迭代器从后往前遍历vector
,输出结果为5 4 3 2 1
。
6.4 常量迭代器
cbegin()
和cend()
返回常量正向迭代器,crbegin()
和crend()
返回常量反向迭代器。常量迭代器不能用于修改元素值,常用于只读遍历。例如:
#include <iostream>
#include <vector>
int main() {
const std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.cbegin(); it != vec.cend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
在这个例子中,vec
是一个常量vector
,只能使用常量迭代器进行遍历。
7. 遍历vector
7.1 使用for
循环和下标
这是最基本的遍历方式,如前面示例中多次使用的:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << " ";
}
std::cout << std::endl;
return 0;
}
7.2 使用迭代器
如前面迭代器部分示例所示,使用正向或反向迭代器遍历vector
:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
7.3 使用范围for
循环(C++11起)
范围for
循环是C++11引入的一种简洁的遍历容器的方式:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在范围for
循环中,num
依次绑定到vec
中的每个元素。如果需要修改元素,可以使用引用:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (int& num : vec) {
num *= 2;
}
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在这个例子中,num
是元素的引用,通过num *= 2
修改了vector
中的每个元素。
8. vector
与算法
C++标准库提供了丰富的算法,这些算法可以与vector
结合使用。例如,使用<algorithm>
头文件中的sort
算法对vector
进行排序:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {5, 3, 1, 4, 2};
std::sort(vec.begin(), vec.end());
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
上述代码使用std::sort
对vec
进行升序排序。除了sort
,还有很多其他算法,如find
用于查找元素,count
用于统计元素个数等,都可以与vector
配合使用。
9. vector
的内存管理
vector
在内部使用动态内存来存储元素。当添加元素导致大小超过容量时,vector
会重新分配内存,将旧元素复制到新的内存位置。这一过程可能会导致性能开销,特别是在频繁插入元素时。为了减少这种开销,可以预先使用reserve
函数分配足够的容量。
当vector
对象被销毁时,它会自动释放所占用的内存。这使得vector
在内存管理方面比普通数组更加安全和方便。
10. 总结
vector
作为C++中常用的模板类,提供了动态数组的功能,具有自动内存管理、方便的元素访问和操作方法。通过掌握vector
的各种用法,包括定义与初始化、元素访问、容量与大小管理、元素插入与删除、迭代器使用、遍历方式以及与算法的结合等,开发者可以更高效地编写C++程序,处理各种数据存储和操作需求。在实际应用中,需要根据具体情况合理使用vector
的特性,以优化程序性能和代码的可读性。