1. 为什么需要vector?
在C++的世界里,数组是最基础的数据结构,但它有个致命缺陷——大小固定。想象你开了一家小店,用固定大小的货架存放商品。生意好时货架不够用,生意差时又浪费空间。vector就是为解决这个问题而生的动态数组,它能够根据需求自动调整大小。
vector的底层实现其实很巧妙。它内部维护了三个关键指针:
_start指向内存块起始位置_finish指向最后一个元素的下一个位置_end_of_storage指向内存块的末尾
这种设计使得vector在大多数情况下都能提供接近原生数组的性能。我曾在性能测试中发现,对于100万次随机访问操作,vector只比原生数组慢约3%,但带来的灵活性却是质的飞跃。
重要提示:虽然vector支持动态扩容,但频繁扩容会导致性能下降。最佳实践是在知道元素数量时,提前用reserve()预分配空间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. vector的核心接口剖析
2.1 基础操作三剑客
vector最常用的三个操作构成了日常使用的"黄金三角":
cpp复制// 创建vector的三种方式
vector<int> v1; // 空vector
vector<int> v2(10); // 10个0
vector<int> v3{1,2,3}; // 初始化列表
// 元素访问的三种方式
int a = v3[1]; // 不检查越界
int b = v3.at(1); // 会抛出异常
int c = v3.front(); // 首元素
在实际项目中,我强烈推荐使用at()而非[],特别是在处理用户输入时。曾经有个bug让我调试了整整一天,最后发现是数组越界导致的未定义行为,换成at()后立即就能定位问题。
2.2 容量管理艺术
vector的容量(capacity)和大小(size)是两个容易混淆的概念:
cpp复制vector<int> v;
v.reserve(100); // 预分配100个元素空间
cout << v.capacity(); // 输出100
cout << v.size(); // 输出0
v.push_back(1);
cout << v.capacity(); // 仍然100
cout << v.size(); // 输出1
经验法则:
- 当元素数量可能很大时,先用reserve预分配空间
- shrink_to_fit()可以释放多余内存,但可能引发重新分配
- capacity()查询当前分配的总空间,size()查询实际元素数量
3. 迭代器与算法搭配
vector的迭代器是随机访问迭代器,这意味着它可以和绝大多数STL算法完美配合:
cpp复制vector<int> nums{3,1,4,2,5};
// 排序
sort(nums.begin(), nums.end());
// 查找
auto it = find(nums.begin(), nums.end(), 4);
if (it != nums.end()) {
cout << "Found at position: " << distance(nums.begin(), it);
}
// 逆序输出
copy(nums.rbegin(), nums.rend(), ostream_iterator<int>(cout, " "));
在实际编码中,我经常使用这种组合来处理数据。比如最近做的一个日志分析工具,就是先用vector存储日志条目,然后用各种STL算法进行统计分析,代码简洁且高效。
4. 模拟实现vector的关键步骤
4.1 基础框架搭建
让我们从最简单的vector骨架开始:
cpp复制template<typename T>
class Vector {
public:
// 类型别名
using iterator = T*;
using const_iterator = const T*;
private:
iterator _start = nullptr; // 指向首元素
iterator _finish = nullptr; //
