1. vector容器基础概念与定义
在C++标准库中,vector是最常用的序列式容器之一,它本质上是一个动态数组,能够自动管理内存并在运行时根据需要调整大小。与普通数组相比,vector的最大优势在于其灵活性——它可以根据元素的增减自动进行内存的重新分配,而程序员无需手动管理内存。
1.1 vector的基本定义方式
定义一个vector需要包含
cpp复制#include <vector>
using namespace std;
// 定义一个整型vector
vector<int> v1;
// 定义时指定初始大小
vector<double> v2(10); // 10个double元素,默认值为0.0
// 定义时指定初始大小和初始值
vector<string> v3(5, "hello"); // 5个string元素,每个都是"hello"
// 通过初始化列表定义
vector<char> v4{'a', 'b', 'c', 'd'};
// 通过另一个vector构造
vector<int> v5(v1.begin(), v1.end());
1.2 vector的内部实现原理
vector在内存中是连续存储的,这意味着它支持随机访问,可以通过下标直接访问任何元素。这种连续存储的特性也使得vector在遍历时具有很高的效率,因为现代CPU的缓存预取机制对这种访问模式非常友好。
vector内部通常维护三个指针:
_Myfirst:指向容器的第一个元素_Mylast:指向最后一个元素的下一个位置_Myend:指向分配的内存末尾
这种设计使得vector能够高效地管理内存,当_Mylast等于_Myend时,表示当前分配的内存已满,需要重新分配更大的内存空间。
1.3 vector与数组的对比
虽然vector和数组都提供连续的存储空间,但它们有几个关键区别:
| 特性 | 数组 | vector |
|---|---|---|
| 大小 | 固定 | 动态可变 |
| 内存管理 | 手动 | 自动 |
| 访问速度 | 稍快 | 稍慢(有额外开销) |
| 功能 | 基本 | 丰富(内置多种成员函数) |
| 安全性 | 较低(可能越界) | 较高(有边界检查) |
在实际开发中,除非有特别严格的性能要求或对内存布局有特殊需求,否则vector通常是更好的选择。
2. vector迭代器的使用与原理
迭代器是STL中最重要的概念之一,它提供了一种统一的方式来访问容器中的元素,而不需要关心容器的具体实现细节。对于vector来说,迭代器本质上就是指针的抽象,但理解迭代器的正确使用方式对于编写安全高效的C++代码至关重要。
2.1 迭代器的基本类型
vector提供了几种不同类型的迭代器,每种都有特定的用途:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
// 普通迭代器(读写)
vector<int>::iterator it = vec.begin();
*it = 10; // 可以修改元素
// 常量迭代器(只读)
vector<int>::const_iterator cit = vec.cbegin();
// *cit = 20; // 错误,不能修改
// 反向迭代器
vector<int>::reverse_iterator rit = vec.rbegin();
cout << *rit; // 输出最后一个元素
// 常量反向迭代器
vector<int>::const_reverse_iterator crit = vec.crbegin();
2.2 迭代器的基本操作
迭代器支持多种操作,使得我们可以方便地遍历和访问容器中的元素:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
// 获取迭代器
auto begin = vec.begin(); // 指向第一个元素
auto end = vec.end(); // 指向最后一个元素的下一个位置
// 遍历vector
for (auto it = begin; it != end; ++it) {
cout << *it << " ";
}
// 算术运算
auto mid = begin + vec.size()/2; // 指向中间元素
cout << "中间元素是:" << *mid;
// 比较迭代器
if (begin < mid) {
cout << "begin在mid前面";
}
2.3 迭代器的失效问题
迭代器失效是使用vector时需要特别注意的问题。当vector进行某些操作(如插入、删除)时,可能会导致现有的迭代器失效。失效的迭代器继续使用会导致未定义行为。
常见的导致迭代器失效的操作包括:
push_back:当引起重新分配时,所有迭代器失效insert:插入点之后的所有迭代器失效(可能全部失效)erase:被删除元素之后的所有迭代器失效reserve:可能导致所有迭代器失效resize:可能导致所有迭代器失效
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2;
vec.push_back(6); // 可能导致it失效(如果引起重新分配)
// cout << *it; // 危险!it可能已经失效
// 安全的做法是在操作后重新获取迭代器
it = vec.begin() + 2;
cout << *it; // 现在安全了
2.4 现代C++中的迭代器使用
C++11及以后版本提供了更简洁的迭代器使用方式:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
// 范围for循环(内部使用迭代器)
for (int num : vec) {
cout << num << " ";
}
// 使用auto简化迭代器声明
for (auto it = vec.begin(); it != vec.end(); ++it) {
cout << *it << " ";
}
// 使用标准算法
#include <algorithm>
for_each(vec.begin(), vec.end(), [](int n) { cout << n << " "; });
3. vector的空间增长机制与性能优化
理解vector的内存管理策略对于编写高效的C++程序至关重要。vector的动态增长特性虽然方便,但如果使用不当可能会导致性能问题。
3.1 vector的容量与大小
vector有两个重要的概念需要区分:
size():当前容器中实际存储的元素数量capacity():容器在不重新分配内存的情况下可以存储的最大元素数量
cpp复制vector<int> vec;
cout << vec.size(); // 输出0
cout << vec.capacity(); // 输出0
vec.push_back(1);
cout << vec.size(); // 输出1
cout << vec.capacity(); // 输出1(具体实现可能不同)
3.2 vector的增长策略
当vector需要增长时(即size即将超过capacity),它会执行以下步骤:
- 分配一块新的、更大的内存空间
- 将现有元素从旧内存复制或移动到新内存
- 释放旧内存
- 更新内部指针
大多数实现采用指数增长策略,通常是当前容量的1.5倍或2倍。这种策略使得插入操作的均摊时间复杂度为O(1)。
cpp复制vector<int> vec;
for (int i = 0; i < 100; ++i) {
vec.push_back(i);
cout << "size: " << vec.size()
<< ", capacity: " << vec.capacity() << endl;
}
3.3 手动管理vector容量
为了避免频繁的内存重新分配,我们可以使用以下方法来优化性能:
cpp复制// 预留足够空间
vector<int> vec;
vec.reserve(100); // 预先分配100个元素的空间
for (int i = 0; i < 100; ++i) {
vec.push_back(i); // 不会引起重新分配
}
// 缩减容量以节省内存
vector<int> vec(100);
vec.resize(10); // size变为10,但capacity可能还是100
vector<int>(vec).swap(vec); // "收缩到适应"技巧
cout << vec.capacity(); // 现在应该等于size()
3.4 移动语义与vector性能
C++11引入的移动语义可以显著提高vector在某些操作中的性能:
cpp复制vector<string> createStrings() {
vector<string> v;
v.reserve(100);
for (int i = 0; i < 100; ++i) {
v.push_back("a very long string...");
}
return v; // 这里会发生NRVO或移动构造,而非复制
}
vector<string> vs = createStrings(); // 高效,不会复制所有字符串
// 使用emplace_back避免临时对象
vector<pair<int, string>> vp;
vp.emplace_back(1, "one"); // 直接在容器内构造,无需复制
4. vector的增删查改操作详解
vector提供了一系列成员函数来操作其中的元素。理解这些操作的性能特征和适用场景对于编写高效代码非常重要。
4.1 添加元素
vector提供了多种添加元素的方法,各有特点:
cpp复制vector<int> vec;
// 在末尾添加元素
vec.push_back(1); // 拷贝或移动插入
vec.emplace_back(2); // 直接在容器内构造,效率更高
// 在指定位置插入元素
auto it = vec.begin();
vec.insert(it, 0); // 在开头插入0
vec.emplace(it + 1, -1); // 在第二个位置构造插入
// 插入多个元素
vec.insert(vec.end(), {3, 4, 5}); // 插入初始化列表
// 从另一个vector插入
vector<int> other = {6, 7, 8};
vec.insert(vec.end(), other.begin(), other.end());
注意:
insert和emplace操作会使插入点之后的所有迭代器失效,且可能导致所有迭代器失效(如果引起重新分配)。
4.2 删除元素
删除操作同样有多种方式,需要根据具体需求选择:
cpp复制vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 删除末尾元素
vec.pop_back();
// 删除指定位置的元素
vec.erase(vec.begin() + 2); // 删除第三个元素
// 删除一个范围内的元素
vec.erase(vec.begin() + 1, vec.begin() + 4); // 删除第2到第4个元素
// 删除所有元素
vec.clear();
// 删除满足条件的元素(C++20起)
erase_if(vec, [](int n) { return n % 2 == 0; }); // 删除所有偶数
注意:
erase操作会使被删除元素之后的所有迭代器失效。删除元素不会减少vector的容量。
4.3 访问元素
vector提供了多种访问元素的方式,各有优缺点:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
// 使用下标访问(不检查边界)
cout << vec[2]; // 输出3
vec[2] = 10; // 修改第三个元素
// 使用at()访问(检查边界)
try {
cout << vec.at(10); // 抛出std::out_of_range异常
} catch (const out_of_range& e) {
cerr << e.what();
}
// 访问首尾元素
cout << vec.front(); // 第一个元素
cout << vec.back(); // 最后一个元素
// 获取底层数组指针(C++11起)
int* p = vec.data();
cout << p[2]; // 等价于vec[2]
4.4 查找元素
vector本身没有提供专用的查找方法,但可以与标准算法配合使用:
cpp复制#include <algorithm>
vector<int> vec = {1, 2, 3, 4, 5, 5, 6};
// 查找单个元素
auto it = find(vec.begin(), vec.end(), 3);
if (it != vec.end()) {
cout << "找到3,位置:" << distance(vec.begin(), it);
}
// 查找满足条件的元素
it = find_if(vec.begin(), vec.end(), [](int n) { return n > 4; });
// 统计元素出现次数
int count = count(vec.begin(), vec.end(), 5);
// 二分查找(必须在有序vector上使用)
sort(vec.begin(), vec.end());
bool exists = binary_search(vec.begin(), vec.end(), 4);
5. 迭代器失效问题深度解析
迭代器失效是使用vector时最常见的陷阱之一,也是面试中经常考察的重点。深入理解迭代器失效的原因和避免方法对于编写健壮的C++代码至关重要。
5.1 什么情况下迭代器会失效
vector的迭代器在以下操作后可能会失效:
-
插入操作:
push_back:如果引起重新分配,所有迭代器失效;否则只有end()迭代器失效insert:插入点之后的所有迭代器失效;如果引起重新分配,所有迭代器失效
-
删除操作:
pop_back:被删除元素的迭代器和end()迭代器失效erase:被删除元素之后的所有迭代器失效
-
容量改变操作:
reserve:如果新容量大于当前容量,所有迭代器可能失效resize:如果新size大于当前capacity,所有迭代器可能失效shrink_to_fit:所有迭代器可能失效
-
交换操作:
swap:两个vector的所有迭代器交换(原迭代器指向另一个容器的元素)
5.2 迭代器失效的后果
使用失效的迭代器会导致未定义行为,常见表现包括:
- 程序崩溃
- 读取或修改了错误的内存位置
- 看似正常工作但产生错误结果
- 在调试版本中可能抛出异常,但在发布版本中静默失败
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2;
vec.push_back(6); // 可能导致重新分配
// cout << *it; // 危险!it可能已经失效
5.3 如何避免迭代器失效问题
-
在修改操作后重新获取迭代器:
cpp复制it = vec.begin() + 2; // 重新获取 cout << *it; // 现在安全了 -
使用索引代替迭代器:
cpp复制size_t pos = 2; vec.push_back(6); cout << vec[pos]; // 使用索引是安全的 -
预留足够容量:
cpp复制vec.reserve(100); // 预先分配足够空间 auto it = vec.begin(); for (int i = 0; i < 50; ++i) { vec.push_back(i); // 不会引起重新分配 // it仍然有效 } -
使用返回值更新迭代器:
cpp复制it = vec.erase(it); // erase返回指向下一个元素的迭代器 it = vec.insert(it, 10); // insert返回指向新插入元素的迭代器
5.4 特殊场景下的迭代器处理
-
循环中删除元素:
cpp复制vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // 错误做法: // for (auto it = vec.begin(); it != vec.end(); ++it) { // if (*it % 2 == 0) { // vec.erase(it); // 错误!erase后it失效,++it未定义 // } // } // 正确做法: for (auto it = vec.begin(); it != vec.end(); ) { if (*it % 2 == 0) { it = vec.erase(it); // 使用返回值更新it } else { ++it; } } -
循环中插入元素:
cpp复制vector<int> vec = {1, 2, 3, 4, 5}; // 在特定条件前插入元素 for (auto it = vec.begin(); it != vec.end(); ++it) { if (*it == 3) { it = vec.insert(it, 0); // 在3前面插入0 ++it; // 跳过新插入的元素,避免无限循环 } }
6. vector相关OJ题目解析与实战
通过解决实际的编程题目是掌握vector使用技巧的最佳方式。下面我们分析几个典型的OJ题目,展示如何灵活运用vector的各种特性来解决问题。
6.1 两数之和(Two Sum)
题目描述:给定一个整数数组nums和一个目标值target,在数组中找出和为目标值的两个整数,并返回它们的下标。
cpp复制vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> num_map;
for (int i = 0; i < nums.size(); ++i) {
int complement = target - nums[i];
if (num_map.find(complement) != num_map.end()) {
return {num_map[complement], i};
}
num_map[nums[i]] = i;
}
return {};
}
关键点:
- 使用哈希表存储已经遍历过的数字及其索引
- 对于每个元素,计算其补数并在哈希表中查找
- 时间复杂度O(n),空间复杂度O(n)
6.2 旋转数组(Rotate Array)
题目描述:将数组向右旋转k个位置,要求原地修改数组。
cpp复制void rotate(vector<int>& nums, int k) {
k %= nums.size();
reverse(nums.begin(), nums.end());
reverse(nums.begin(), nums.begin() + k);
reverse(nums.begin() + k, nums.end());
}
关键点:
- 使用三次反转实现旋转效果
- 注意k可能大于数组长度的情况
- 时间复杂度O(n),空间复杂度O(1)
6.3 合并两个有序数组(Merge Sorted Array)
题目描述:将两个有序数组合并到第一个数组中,第一个数组有足够的空间。
cpp复制void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = m - 1, j = n - 1, k = m + n - 1;
while (i >= 0 && j >= 0) {
nums1[k--] = (nums1[i] > nums2[j]) ? nums1[i--] : nums2[j--];
}
while (j >= 0) {
nums1[k--] = nums2[j--];
}
}
关键点:
- 从后向前合并,避免覆盖nums1中的元素
- 处理剩余元素的拷贝
- 时间复杂度O(m+n),空间复杂度O(1)
6.4 最大子序和(Maximum Subarray)
题目描述:找到一个具有最大和的连续子数组。
cpp复制int maxSubArray(vector<int>& nums) {
int max_sum = nums[0], current_sum = nums[0];
for (int i = 1; i < nums.size(); ++i) {
current_sum = max(nums[i], current_sum + nums[i]);
max_sum = max(max_sum, current_sum);
}
return max_sum;
}
关键点:
- 使用Kadane算法
- 维护当前子数组和和最大子数组和
- 时间复杂度O(n),空间复杂度O(1)
6.5 删除排序数组中的重复项(Remove Duplicates from Sorted Array)
题目描述:原地删除排序数组中的重复项,返回新长度。
cpp复制int removeDuplicates(vector<int>& nums) {
if (nums.empty()) return 0;
int i = 0;
for (int j = 1; j < nums.size(); ++j) {
if (nums[j] != nums[i]) {
nums[++i] = nums[j];
}
}
return i + 1;
}
关键点:
- 使用双指针技巧
- 慢指针i记录唯一元素的位置
- 时间复杂度O(n),空间复杂度O(1)
7. vector的高级用法与性能优化技巧
掌握了vector的基本用法后,我们来看一些高级技巧和性能优化方法,这些技巧在实际项目开发中非常有用。
7.1 使用swap释放内存
vector的clear()方法只清空元素,不释放内存。要真正释放内存,可以使用swap技巧:
cpp复制vector<int> vec(1000000);
// ...使用vec...
vec.clear(); // 清空元素,但capacity()不变
cout << vec.capacity(); // 仍然是1000000
// 使用swap释放内存
vector<int>().swap(vec);
cout << vec.capacity(); // 现在是0
// C++11后更推荐shrink_to_fit
vec.shrink_to_fit();
7.2 高效初始化大型vector
初始化大型vector时,直接使用reserve和emplace_back可以避免不必要的拷贝:
cpp复制// 低效做法:先默认构造,再赋值
vector<BigObject> vec1(1000); // 1000次默认构造
for (auto& obj : vec1) {
obj = createBigObject(); // 1000次拷贝赋值
}
// 高效做法:直接构造
vector<BigObject> vec2;
vec2.reserve(1000);
for (int i = 0; i < 1000; ++i) {
vec2.emplace_back(createBigObject()); // 直接构造
}
7.3 使用移动语义优化vector操作
C++11的移动语义可以显著提高vector在某些操作中的性能:
cpp复制vector<string> createLargeVector() {
vector<string> v;
// ...填充大量数据...
return v; // 这里会发生NRVO或移动构造
}
vector<string> vs = createLargeVector(); // 高效,不会复制所有元素
// 移动元素而非拷贝
vector<string> vs2;
vs2.push_back(std::move(vs[0])); // 移动而非拷贝
7.4 自定义分配器
对于特殊的内存需求,可以为vector提供自定义分配器:
cpp复制template <typename T>
class MyAllocator {
// 实现分配器接口...
};
vector<int, MyAllocator<int>> customVec;
7.5 多维vector的使用
vector可以嵌套使用来表示多维数组:
cpp复制// 二维vector
vector<vector<int>> matrix(10, vector<int>(20)); // 10x20矩阵
// 初始化二维vector
vector<vector<int>> maze = {
{1, 0, 1, 1},
{1, 1, 0, 1},
{0, 1, 1, 1}
};
// 三维vector
vector<vector<vector<int>>> cube(10, vector<vector<int>>(20, vector<int>(30)));
7.6 vector与C风格数组的互操作
vector可以与C风格数组相互转换:
cpp复制// vector转C数组
vector<int> vec = {1, 2, 3, 4, 5};
int* arr = vec.data(); // 获取底层数组指针
// C数组转vector
int carr[] = {1, 2, 3, 4, 5};
vector<int> vec2(carr, carr + sizeof(carr)/sizeof(carr[0]));
8. vector在不同场景下的最佳实践
根据不同的使用场景,vector的最佳使用方式也有所不同。下面我们分析几种常见场景下的最佳实践。
8.1 作为函数参数和返回值
传递vector给函数时,根据需求选择合适的传递方式:
cpp复制// 只读访问:使用const引用
void printVector(const vector<int>& vec) {
for (int n : vec) cout << n << " ";
}
// 需要修改:使用普通引用
void modifyVector(vector<int>& vec) {
vec.push_back(10);
}
// 需要复制:传值(C++11后可以利用移动语义)
vector<int> processVector(vector<int> vec) {
// 处理vec...
return vec; // NRVO或移动
}
// 返回大型vector:直接返回,不要返回指针或引用
vector<int> createLargeVector() {
vector<int> v(1000000);
// ...填充数据...
return v; // 高效,不会复制
}
8.2 在性能敏感代码中的使用
在性能敏感的场景下,使用vector需要注意:
- 预先分配足够空间:避免频繁重新分配
- 使用emplace_back代替push_back:避免临时对象
- 避免在循环中反复调用size():
cpp复制// 不好 for (size_t i = 0; i < vec.size(); ++i) { ... } // 更好 size_t size = vec.size(); for (size_t i = 0; i < size; ++i) { ... } - 考虑使用reserve和shrink_to_fit:精确控制内存使用
8.3 在多线程环境中的使用
vector本身不是线程安全的,在多线程环境中使用时需要额外注意:
- 读操作:多个线程可以同时读取vector,前提是没有线程在修改它
- 写操作:任何写操作都需要独占访问
- 迭代器失效:一个线程修改vector可能导致另一个线程的迭代器失效
cpp复制vector<int> shared_vec;
mutex vec_mutex;
// 线程安全的添加元素
void safe_add(int value) {
lock_guard<mutex> guard(vec_mutex);
shared_vec.push_back(value);
}
// 线程安全的读取
void safe_read() {
lock_guard<mutex> guard(vec_mutex);
for (int n : shared_vec) {
// 处理n...
}
}
8.4 在嵌入式或资源受限环境中的使用
在资源受限的环境中,使用vector需要注意:
- 避免频繁内存分配:预先reserve足够空间
- 限制最大大小:可以使用自定义分配器或封装类
- 考虑使用静态vector:基于数组的固定大小vector替代方案
- 谨慎使用异常:某些嵌入式环境可能禁用异常
cpp复制template <typename T, size_t MaxSize>
class StaticVector {
T data[MaxSize];
size_t size = 0;
public:
void push_back(const T& value) {
if (size >= MaxSize) { /* 处理错误 */ }
data[size++] = value;
}
// ...其他vector类似接口...
};
StaticVector<int, 100> vec; // 固定大小100的"vector"
9. vector的常见问题与调试技巧
即使是有经验的C++开发者,在使用vector时也会遇到各种问题。下面总结一些常见问题及其解决方法。
9.1 常见编译错误
-
迭代器类型不匹配:
cpp复制vector<int>::const_iterator cit = vec.begin(); // 需要vec.cbegin() -
越界访问:
cpp复制vector<int> vec; cout << vec[0]; // 未定义行为 cout << vec.at(0); // 抛出std::out_of_range -
模板参数错误:
cpp复制vector vec; // C++17前需要指定模板参数 vector<int> vec; // 正确
9.2 常见运行时错误
-
迭代器失效:
cpp复制auto it = vec.begin(); vec.push_back(1); // 可能导致it失效 cout << *it; // 未定义行为 -
内存不足:
cpp复制try { vector<int> vec; vec.resize(1000000000000); // 可能抛出std::bad_alloc } catch (const bad_alloc& e) { cerr << "内存不足: " << e.what(); } -
类型不匹配:
cpp复制vector<string> vec = {1, 2, 3}; // 无法从int构造string
9.3 调试技巧
-
打印vector内容:
cpp复制template <typename T> void printVector(const vector<T>& vec) { cout << "size=" << vec.size() << ", capacity=" << vec.capacity() << ": ["; for (const auto& x : vec) cout << x << " "; cout << "]\n"; } -
检查迭代器有效性:
cpp复制auto it = vec.begin(); vec.push_back(1); if (it == vec.begin()) { // 不可靠的检查 // ... } -
使用at()进行调试:
cpp复制// 在调试版本中使用at()捕获越界访问 #ifndef NDEBUG #define SAFE_VECTOR_ACCESS 1 #endif #if SAFE_VECTOR_ACCESS cout << vec.at(10); #else cout << vec[10]; #endif
9.4 性能分析工具
-
容量监控:
cpp复制vector<int> vec; for (int i = 0; i < 100; ++i) { vec.push_back(i); cout << "size=" << vec.size() << ", capacity=" << vec.capacity() << "\n"; } -
使用性能分析器:
- Valgrind(Linux)
- Visual Studio Profiler(Windows)
- Xcode Instruments(macOS)
-
自定义分配器跟踪:
cpp复制template <typename T> class TracingAllocator { // ...实现分配器接口... // 在分配/释放时打印日志 }; vector<int, TracingAllocator<int>> vec;
10. C++20/23中vector的新特性
C++20和即将到来的C++23为vector引入了一些新特性,了解这些新特性可以帮助我们编写更现代、更高效的代码。
10.1 C++20中的新特性
-
constexpr支持:
vector的部分操作现在可以在编译期执行:cpp复制constexpr vector<int> createVector() { vector<int> v = {1, 2, 3}; v.push_back(4); return v; } constexpr auto vec = createVector(); -
范围擦除:
更简洁的删除元素方式:cpp复制vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // 删除所有偶数 erase_if(vec, [](int n) { return n % 2 == 0; }); // 等价于 vec.erase(remove_if(vec.begin(), vec.end(), [](int n) { return n % 2 == 0; }), vec.end()); -
三路比较运算符:
cpp复制vector<int> v1 = {1, 2, 3}; vector<int> v2 = {1, 2, 4}; auto cmp = v1 <=> v2; // 返回std::strong_ordering
10.2 C++23中的预期特性
-
resize_and_overwrite:
更高效的重置大小并填充内容:cpp复制vector<int> vec = {1, 2, 3}; vec.resize_and_overwrite(5, [](auto* p, auto n) { for (size_t i = 0; i < n; ++i) { p[i] = i * 2; } return n; }); -
更丰富的范围操作:
与范围库更深度集成:cpp复制vector<int> vec = {1, 2, 3, 4, 5}; auto even = vec | views::filter([](int n) { return n % 2 == 0; }); -
可能加入的静态vector:
编译期固定大小的vector类似物:cpp复制static_vector<int, 10> vec; // 最大容量10的vector
10.3 现代C++中的vector使用建议
-
优先使用现代C++特性:
- 用emplace_back代替push_back
- 用范围for代替显式迭代器循环
- 用auto简化类型声明
-
结合其他现代C++特性使用:
cpp复制vector<unique_ptr<Resource>> resources; resources.emplace_back(make_unique<Resource>()); // 使用结构化绑定 for (const auto& [index, value] : views::enumerate(vec)) { cout << index << ": " << value << "\n"; } -
考虑性能与安全的平衡:
- 调试版本中使用at()进行边界检查
- 发布版本中使用operator[]获得最佳性能
- 使用static_assert检查类型要求
cpp复制// 确保类型是可移动构造的
static_assert(is_move_constructible_v<MyType>,
"MyType must be move constructible");
vector<MyType> vec;
