- gf24240 的博客
《梦溪笔谈·C++》卷八:向量——vector
- 2025-4-5 11:13:08 @
Q:c++中向量的用法?
A:
在 C++ 中,向量(Vector) 是一种动态数组,属于标准模板库(STL)的一部分,位于 <vector>
头文件中。它提供了自动内存管理、动态扩容和丰富的成员函数,是 C++ 中最常用的容器之一。以下是向量的核心用法详解:
一、基本操作
1. 创建和初始化
#include <vector>
using namespace std;
// 空向量
vector<int> vec1;
// 指定大小和初始值
vector<int> vec2(5, 0); // 5个元素,初始化为0: [0, 0, 0, 0, 0]
// 列表初始化 (C++11)
vector<int> vec3 = {1, 2, 3}; // [1, 2, 3]
// 从数组或另一个向量初始化
int arr[] = {4, 5, 6};
vector<int> vec4(arr, arr + 3); // [4, 5, 6]
vector<int> vec5(vec3); // 拷贝vec3
2. 元素访问
方法 | 说明 |
---|---|
vec[i] |
访问第i个元素(不检查越界) |
vec.at(i) |
访问第i个元素(越界抛出异常) |
vec.front() |
第一个元素 |
vec.back() |
最后一个元素 |
vec.data() |
返回指向底层数组的指针(C++11) |
vector<int> vec = {10, 20, 30};
cout << vec[1]; // 20
cout << vec.at(2); // 30
cout << vec.front(); // 10
cout << vec.back(); // 30
3. 修改元素
vec[1] = 50; // 直接修改: [10, 50, 30]
vec.push_back(40); // 末尾添加: [10, 50, 30, 40]
vec.pop_back(); // 删除末尾: [10, 50, 30]
vec.insert(vec.begin() + 1, 15); // 在位置1插入15: [10, 15, 50, 30]
vec.erase(vec.begin() + 2); // 删除位置2: [10, 15, 30]
vec.clear(); // 清空向量: []
二、容量管理
方法 | 说明 |
---|---|
vec.size() |
当前元素个数 |
vec.empty() |
判断是否为空 |
vec.capacity() |
当前分配的存储容量(≥ size) |
vec.reserve(n) |
预分配容量(避免频繁扩容) |
vec.shrink_to_fit() |
释放多余容量(C++11) |
vector<int> vec;
vec.reserve(100); // 预分配100个元素的空间
cout << vec.capacity(); // 100
vec.push_back(1);
vec.shrink_to_fit(); // 容量调整为1
三、迭代器遍历
vector<int> vec = {1, 2, 3};
// 1. 常规for循环
for (size_t i = 0; i < vec.size(); ++i) {
cout << vec[i] << " ";
}
// 2. 迭代器 (推荐)
for (auto it = vec.begin(); it != vec.end(); ++it) {
cout << *it << " ";
}
// 3. 范围for循环 (C++11)
for (int num : vec) {
cout << num << " ";
}
四、动态扩容机制
- 向量在插入元素时(如
push_back
),若当前容量不足,会按一定策略(通常为2倍)自动扩容。 - 扩容代价:重新分配内存 + 拷贝原有元素。
- 优化建议:若已知元素数量,优先使用
reserve()
预分配空间。
五、常用算法(需 <algorithm>
)
#include <algorithm>
vector<int> vec = {3, 1, 4, 2};
// 排序
sort(vec.begin(), vec.end()); // [1, 2, 3, 4]
// 反转
reverse(vec.begin(), vec.end()); // [4, 3, 2, 1]
// 查找(返回迭代器)
auto it = find(vec.begin(), vec.end(), 3);
if (it != vec.end()) {
cout << "Found at index: " << it - vec.begin();
}
// 累加(需 `<numeric>`)
int sum = accumulate(vec.begin(), vec.end(), 0);
六、性能与注意事项
- 随机访问:O(1) 时间复杂度(与数组相同)。
- 插入/删除:
- 末尾操作(
push_back
/pop_back
):O(1)。 - 中间或头部操作:O(n)(需移动元素)。
- 末尾操作(
- 内存连续性:向量元素在内存中连续存储,适合需要快速遍历的场景。
- 线程安全:非线程安全容器,多线程环境需加锁。
七、与数组的对比
特性 | 向量 (vector ) |
原生数组 (int[] ) |
---|---|---|
动态大小 | 支持自动扩容/缩容 | 固定大小 |
内存管理 | 自动释放内存 | 需手动管理 |
功能接口 | 丰富(如 push_back 、sort ) |
无内置方法 |
性能 | 略慢(因封装) | 更快(直接内存访问) |
八、典型应用场景
- 替代原生数组(尤其大小不确定时)。
- 需要频繁在末尾增删元素(如栈或队列模拟)。
- 作为函数返回值或参数(避免手动内存管理)。
Q:C++怎么用vector作为函数返回值
A:
在 C++ 中,使用 vector
作为函数返回值非常方便,因为 vector
支持移动语义(C++11 引入),可以高效地返回局部变量而无需担心性能问题。以下是详细用法和注意事项:
一、直接返回局部 vector
基本语法
#include <vector>
using namespace std;
// 返回一个vector的函数
vector<int> createVector() {
vector<int> vec = {1, 2, 3};
return vec; // 触发移动语义(非拷贝)
}
int main() {
vector<int> result = createVector();
for (int num : result) {
cout << num << " "; // 输出: 1 2 3
}
return 0;
}
关键点
- 移动语义优化:
C++11 后,返回局部vector
会优先触发移动构造(而非拷贝),效率与返回指针/引用相当。 - 编译器优化(NRVO):
多数编译器会进一步优化(Named Return Value Optimization),直接避免构造临时对象。
二、返回动态生成的 vector
动态填充后返回
vector<int> generateSquares(int n) {
vector<int> squares;
for (int i = 1; i <= n; ++i) {
squares.push_back(i * i);
}
return squares; // 依然高效
}
通过参数返回(不推荐)
void generateSquares(int n, vector<int>& out) {
out.clear();
for (int i = 1; i <= n; ++i) {
out.push_back(i * i);
}
}
// 调用时需先构造空vector
vector<int> squares;
generateSquares(5, squares);
缺点:代码冗余,不如直接返回值清晰。
三、返回大 vector 的性能验证
对比拷贝与移动
#include <vector>
#include <iostream>
using namespace std;
vector<int> createLargeVector() {
vector<int> vec(1000000, 42); // 100万个元素
return vec;
}
int main() {
vector<int> v;
cout << "Before assignment, v.capacity(): " << v.capacity() << endl;
v = createLargeVector(); // 移动赋值(非拷贝)
cout << "After assignment, v.capacity(): " << v.capacity() << endl;
return 0;
}
输出:
Before assignment, v.capacity(): 0
After assignment, v.capacity(): 1000000
结论:实际发生了移动操作,而非深拷贝。
四、返回多维 vector
返回二维 vector
vector<vector<int>> generateMatrix(int rows, int cols) {
vector<vector<int>> matrix(rows, vector<int>(cols, 0));
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
matrix[i][j] = i + j;
}
}
return matrix;
}
调用示例
auto mat = generateMatrix(3, 3);
for (auto& row : mat) {
for (int val : row) {
cout << val << " ";
}
cout << endl;
}
输出:
0 1 2
1 2 3
2 3 4
五、注意事项
-
避免返回局部静态 vector
vector<int>& badExample() { static vector<int> vec = {1, 2, 3}; // 静态变量 return vec; // 可能引发线程安全问题 }
问题:多线程环境下不安全,且所有调用共享同一数据。
-
不要返回指向局部 vector 的指针/引用
vector<int>* dangerousExample() { vector<int> vec = {1, 2, 3}; return &vec; // 错误!vec将被销毁 }
问题:局部变量离开作用域后指针悬空。
-
明确所有权语义(C++11后无需担心)
- 优先使用返回值而非输出参数。
- 需要修改外部变量时,使用引用参数。
六、性能最佳实践
- 小对象:直接返回值(编译器优化足够高效)。
- 大对象:依然直接返回(移动语义保证效率)。
- 需要复用内存:通过参数传入
vector
并复用其内存:void fillVector(vector<int>& out) { out.assign({1, 2, 3}); // 复用现有内存 }
总结
场景 | 推荐方式 | 效率 |
---|---|---|
返回临时结果 | 直接返回 vector |
⭐⭐⭐ |
需要复用内存 | 通过引用参数传递 vector |
⭐⭐ |
多线程共享数据 | 返回副本或使用线程安全容器 | ⭐ |
现代 C++ 中,优先使用返回值而非输出参数!