1. 项目概述
作为一名C++开发者,STL容器是我们日常开发中不可或缺的工具。其中,vector作为最常用的动态数组容器,其重要性不言而喻。但很多开发者仅仅停留在"会用"层面,对其底层实现原理知之甚少。本文将深入剖析vector的使用方法及其模拟实现,帮助读者真正掌握这个强大的容器。
vector的核心优势在于它结合了普通数组的高效随机访问特性,又解决了固定大小的限制。它通过动态内存管理实现了自动扩容,使得开发者可以专注于业务逻辑而无需担心内存管理问题。理解vector的底层实现不仅能帮助我们更好地使用它,还能提升我们的内存管理能力和算法设计水平。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. vector的使用详解
2.1 vector的基本特性
vector是C++标准模板库(STL)中的一个序列容器,它封装了动态大小数组的功能。与普通数组相比,vector具有以下显著特点:
- 动态扩容:当元素数量超过当前容量时,vector会自动分配更大的内存空间
- 随机访问:支持通过下标直接访问任意元素,时间复杂度为O(1)
- 连续存储:所有元素在内存中是连续存储的,这带来了良好的缓存局部性
- 类型安全:作为模板类,vector可以存储任意类型的元素,同时保证类型安全
vector的底层实现通常使用三个指针来管理内存:
- _start:指向数组的首元素
- _finish:指向最后一个元素的下一个位置
- _end_of_storage:指向分配的内存末尾
这种设计使得size()可以通过_finish - _start计算得到,capacity()则通过_end_of_storage - _start计算。
2.2 vector的构造函数
vector提供了多种构造函数来满足不同的初始化需求:
2.2.1 默认构造函数
cpp复制vector<int> v1; // 创建一个空的vector
默认构造函数创建一个不包含任何元素的vector,所有指针都初始化为nullptr。这是最轻量级的构造方式,适用于后续通过push_back等操作逐步添加元素的场景。
2.2.2 填充构造函数
cpp复制vector<int> v2(5, 1); // 创建包含5个1的vector
这个构造函数接受两个参数:元素数量n和初始值val。它会创建包含n个val的vector。值得注意的是,val参数有一个默认值T(),即类型T的默认构造值。这种设计既支持内置类型也支持自定义类型。
2.2.3 迭代器区间构造函数
cpp复制vector<int> v3(v2.begin(), v2.end()); // 用v2的范围构造v3
这个构造函数模板可以接受任意迭代器区间,包括其他容器的迭代器。例如:
cpp复制string s = "hello";
vector<char> v(s.begin(), s.end()); // 用string构造vector<char>
2.2.4 拷贝构造函数
cpp复制vector<int> v4(v3); // 拷贝构造v4
拷贝构造函数会创建一个与参数vector完全相同的副本,包括所有元素和容量信息。
2.3 vector的扩容机制
vector的动态扩容是其核心特性之一。当插入新元素导致size() == capacity()时,vector会执行扩容操作。扩容的基本流程是:
- 分配新的更大的内存空间
- 将原有元素拷贝到新空间
- 释放旧空间
- 更新指针指向新空间
不同编译器的扩容策略有所不同:
- VS编译器通常采用1.5倍扩容
- g++通常采用2倍扩容
这种差异源于不同的性能优化考虑。1.5倍扩容可以减少内存浪费,而2倍扩容可以减少扩容次数。
可以通过以下代码观察扩容过程:
cpp复制void TestVectorExpand() {
size_t sz;
vector<int> v;
sz = v.capacity();
cout << "making v grow:\n";
for (int i = 0; i < 1000; ++i) {
v.push_back(i);
if (sz != v.capacity()) {
sz = v.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
2.4 vector的遍历方式
vector支持三种主要的遍历方式:
2.4.1 下标访问
cpp复制for (int i = 0; i < v.size(); ++i)
cout << v[i] << " ";
这种方式利用了vector支持随机访问的特性,效率最高。但要注意下标越界问题。
2.4.2 迭代器遍历
cpp复制vector<int>::iterator it = v.begin();
while (it != v.end()) {
cout << *it << " ";
++it;
}
迭代器提供了统一的容器访问接口,是STL的核心概念之一。
2.4.3 范围for循环
cpp复制for (auto& e : v)
cout << e << " ";
范围for是C++11引入的语法糖
