跳到主要内容C++ vector 容器底层原理与模拟实现 | 极客日志C++算法
C++ vector 容器底层原理与模拟实现
综述由AI生成C++ vector 容器基于动态数组实现,通过三个指针管理内存。解析了 vector 的构造函数、赋值运算符、容量操作及元素访问修改操作。重点阐述了迭代器失效问题:扩容时所有迭代器失效,erase 后删除位置及后续迭代器失效,需使用 erase 返回值更新迭代器。提供了完整的 vector 模拟实现代码,涵盖命名空间 bit 下的类定义及测试用例,展示了现代 C++ 写法如 swap 交换资源及异常安全机制。
随缘34 浏览 1 模板的使用说明
在 C++ 中,模板是实现泛型编程的重要工具,它允许我们编写与数据类型无关的代码。vector 容器正是通过模板技术实现的,可以存储任意类型的数据:
template<class T> class vector {
这里的 T 是类型参数,当我们创建 vector 对象时,需要指定具体的类型
vector<int> intVector;
vector<string> strVector;
vector<double> dblVector;
模板的使用让 vector 具有了极强的通用性,可以容纳各种数据类型,从基本类型到自定义类类型。
2 vector 深度剖析及模拟实现
2.1 vector 的成员变量
vector 底层使用动态数组实现,通过三个指针来管理内存:
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
在这里,给了三个指针缺省值 nullptr 初始化。
2.2 构造函数
vector 提供了多种构造函数来满足不同的初始化需求:
2.2.1 指定大小和初始值的构造函数
vector(size_t n, const T& val = T()) {
reserve(n);
for (size_t i = 0; i < n; i++) {
push_back(val);
}
}
使用示例:
vector<int> v1;
;
;
(5)
vector<int> v2(3, 10)
vector<string> v3(2, "hello")
2.2.2 迭代器范围构造函数
template <class InputIterator>
vector(InputIterator first, InputIterator last) {
while (first != last) {
push_back(*first);
++first;
}
}
这个构造函数是函数模板,可以接受任意类型的迭代器:
int arr[] = {1, 2, 3, 4, 5};
vector<int> v1(arr, arr + 5);
vector<int> v2(v1.begin(), v1.end());
2.2.3 拷贝构造函数(现代写法)
vector(const vector<T>& v) {
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
现代写法通过创建临时对象并交换资源,代码简洁且异常安全。
2.3 赋值运算符重载
vector<T>& operator=(vector<T> tmp) {
swap(tmp);
return *this;
}
2.4 容量相关操作
2.4.1 reserve 开空间
void reserve(size_t n) {
if (n > capacity()) {
size_t sz = size();
T* tmp = new T[n];
if (_start) {
for (size_t i = 0; i < sz; i++) {
std::swap(tmp[i], _start[i]);
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
- 使用
std::swap 而不是直接赋值,这样可以高效地交换资源。
- 如果直接使用
memcpy,对于管理资源的类(如 string)会导致浅拷贝问题!
- 重新计算
_finish 和 _end_of_storage 指针
2.4.2 用 resize 调整大小
void resize(size_t n, T val = T()) {
if (n < size()) {
_finish = _start + n;
} else {
reserve(n);
while (_finish < _start + n) {
*_finish = val;
++_finish;
}
}
}
vector<int> v = {1, 2, 3};
v.resize(5);
v.resize(2);
2.4.3 size 和 capacity
size_t capacity() const { return _end_of_storage - _start; }
size_t size() const { return _finish - _start; }
2.5 元素访问操作
2.5.1 下标运算符
T& operator[](size_t i) {
assert(i < size());
return _start[i];
}
const T& operator[](size_t i) const {
assert(i < size());
return _start[i];
}
提供 const 和非 const 版本,支持读写访问和只读访问。
2.6 修改操作
2.6.1 push_back 尾部添加元素
void push_back(const T& x) {
if (_finish == _end_of_storage) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
扩容策略:初始为 0 时分配 4 个元素空间,否则每次扩容为原来的 2 倍。
2.6.2 pop_back 尾部删除元素
void pop_back() {
assert(!empty());
--_finish;
}
2.6.3 insert 在指定位置插入元素
iterator insert(iterator pos, const T& x) {
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _end_of_storage) {
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iterator end = _finish - 1;
while (end >= pos) {
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
扩容后需要重新计算 pos 的位置,因为_start 可能改变了。
2.6.4 erase 删除指定位置元素
iterator erase(iterator pos) {
assert(pos >= _start);
assert(pos < _finish);
iterator it = pos + 1;
while (it != _finish) {
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
3 vector 迭代器失效问题
迭代器失效是使用 vector 时需要特别注意的问题,不当使用会导致未定义行为。
3.1 insert 失效问题
当在 vector 中插入元素时,如果引起扩容,所有迭代器都会失效。
void test_vector2() {
bit::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
auto it = v.begin() + 3;
v.insert(v.begin(), 0);
}
3.2 erase 失效问题
erase 操作会使被删除元素及其后面所有元素的迭代器失效。
void test_vector3() {
bit::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
auto it = v.begin() + 2;
v.erase(v.begin());
}
3.3 删除所有的偶数
void test_vector4() {
bit::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
auto it = v.begin();
while (it != v.end()) {
if (*it % 2 == 0) {
it = v.erase(it);
} else {
++it;
}
}
}
3.4 不同编译器对迭代器失效的检查
- Debug 模式下通常会进行严格的迭代器检查
- Release 模式下可能没有检查,导致难以发现的 bug
- Linux 的 gcc 编译器检查就会比较宽松
3.5 扩容之后,迭代器已经失效了
vector<int> v = {1, 2, 3};
auto it = v.begin();
v.push_back(4);
3.6 erase 删除的迭代器如果是最后一个元素
vector<int> v = {1, 2, 3};
auto it = v.begin() + 2;
it = v.erase(it);
3.7 string 的迭代器失效
与 vector 类似,string 在插入、扩容操作、erase 之后,迭代器也会失效。
4 本文代码完整展示
(一)vector.h
#pragma once
#include <iostream>
#include <cassert>
using namespace std;
namespace bit {
template<class T>
class vector {
public:
typedef T* iterator;
typedef const T* const_iterator;
iterator begin() { return _start; }
iterator end() { return _finish; }
const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }
vector(size_t n, const T& val = T()) {
reserve(n);
for (size_t i = 0; i < n; i++) {
push_back(val);
}
}
vector(int n, const T& val = T()) {
reserve(n);
for (int i = 0; i < n; i++) {
push_back(val);
}
}
template <class InputIterator>
vector(InputIterator first, InputIterator last) {
while (first != last) {
push_back(*first);
++first;
}
}
vector() = default;
vector(const vector<T>& v) {
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
vector<T>& operator=(vector<T> tmp) {
swap(tmp);
return *this;
}
void swap(vector<T>& v) {
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
void clear() { _finish = _start; }
bool empty() const { return _start == _finish; }
void reserve(size_t n) {
if (n > capacity()) {
size_t sz = size();
T* tmp = new T[n];
if (_start) {
for (size_t i = 0; i < sz; i++) {
std::swap(tmp[i], _start[i]);
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
void resize(size_t n, T val = T()) {
if (n < size()) {
_finish = _start + n;
} else {
reserve(n);
while (_finish < _start + n) {
*_finish = val;
++_finish;
}
}
}
size_t capacity() const { return _end_of_storage - _start; }
size_t size() const { return _finish - _start; }
T& operator[](size_t i) {
assert(i < size());
return _start[i];
}
const T& operator[](size_t i) const {
assert(i < size());
return _start[i];
}
void push_back(const T& x) {
if (_finish == _end_of_storage) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
void pop_back() {
assert(!empty());
--_finish;
}
iterator insert(iterator pos, const T& x) {
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _end_of_storage) {
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iterator end = _finish - 1;
while (end >= pos) {
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
iterator erase(iterator pos) {
assert(pos >= _start);
assert(pos < _finish);
iterator it = pos + 1;
while (it != _finish) {
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
~vector() {
if (_start) {
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
}
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
};
}
(二)test.cpp
#include "vector.h"
#include <iostream>
using namespace std;
namespace bit {
void Print(const vector<int>& v) {
for (auto e : v) {
cout << e << " ";
}
cout << endl;
}
void test_vector1() {
cout << "测试基本操作:" << endl;
bit::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v[0]++;
Print(v);
for (auto e : v) {
cout << e << " ";
}
cout << endl;
}
void test_vector2() {
cout << "\n测试 insert 操作:" << endl;
bit::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
Print(v);
v.insert(v.begin(), 0);
Print(v);
auto it = v.begin() + 3;
v.insert(it, 30);
Print(v);
}
void test_vector3() {
cout << "\n测试 erase 操作:" << endl;
bit::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
Print(v);
v.erase(v.begin());
Print(v);
auto it = v.begin() + 2;
v.erase(it);
Print(v);
}
void test_vector4() {
cout << "\n测试删除所有偶数:" << endl;
bit::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
cout << "删除前: ";
for (auto e : v) {
cout << e << " ";
}
cout << endl;
auto it = v.begin();
while (it != v.end()) {
if (*it % 2 == 0) {
it = v.erase(it);
} else {
++it;
}
}
cout << "删除后: ";
for (auto e : v) {
cout << e << " ";
}
cout << endl;
}
void test_vector5() {
cout << "\n测试 resize 操作:" << endl;
bit::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
cout << "原始 vector: ";
for (auto e : v) {
cout << e << " ";
}
cout << endl;
v.resize(3);
cout << "resize(3): ";
for (auto e : v) {
cout << e << " ";
}
cout << endl;
v.resize(20, 5);
cout << "resize(20, 5): ";
for (auto e : v) {
cout << e << " ";
}
cout << endl;
}
void test_vector6() {
cout << "\n测试拷贝构造和赋值:" << endl;
bit::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
cout << "v1: ";
for (auto e : v1) {
cout << e << " ";
}
cout << endl;
bit::vector<int> v2(v1);
cout << "v2(拷贝 v1): ";
for (auto e : v2) {
cout << e << " ";
}
cout << endl;
bit::vector<int> v3 = {10, 20, 30, 40};
v1 = v3;
cout << "v1(赋值后): ";
for (auto e : v1) {
cout << e << " ";
}
cout << endl;
bit::vector<int> v4(10, 1);
cout << "v4(10 个 1): ";
for (auto e : v4) {
cout << e << " ";
}
cout << endl;
bit::vector<char> v5(10, 'x');
cout << "v5(10 个 x): ";
for (auto e : v5) {
cout << e << " ";
}
cout << endl;
}
void test_vector7() {
cout << "\n测试 string 类型 vector:" << endl;
bit::vector<string> v1;
v1.push_back("111111111111111111111111");
v1.push_back("222222222222222222222222");
v1.push_back("333333333333333333333333");
v1.push_back("444444444444444444444444");
v1.push_back("555555555555555555555555");
for (auto& e : v1) {
cout << e << " ";
}
cout << endl;
}
}
int main() {
bit::test_vector1();
bit::test_vector2();
bit::test_vector3();
bit::test_vector4();
bit::test_vector5();
bit::test_vector6();
bit::test_vector7();
return 0;
}
C++ vector 基于动态数组实现,通过三个指针管理内存。核心风险是迭代器失效:insert 可能扩容使所有迭代器失效;erase 使被删位置后迭代器失效,必须通过 it=erase(it) 接收返回值。采用 2 倍扩容策略,现代写法通过 swap 实现安全拷贝。连续存储支持高效随机访问,但需警惕迭代器失效这一主要陷阱。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online