

💗博主介绍:计算机专业的一枚大学生 来自重庆 @燃于AC之乐✌专注于C++技术栈,算法,竞赛领域,技术学习和项目实战✌💗
💗根据博主的学习进度更新(可能不及时)
💗后续更新主要内容:C语言,数据结构,C++、linux(系统编程和网络编程)、MySQL、Redis、QT、Python、Git、爬虫、数据可视化、小程序、AI大模型接入,C++实战项目与学习分享。
👇🏻 精彩专栏 推荐订阅👇🏻
点击进入🌌作者专栏🌌:
Linux系统编程✅
算法画解✅
C++✅🌟算法相关题目点击即可进入实操🌟
感兴趣的可以先收藏起来,请多多支持,还有大家有相关问题都可以给我留言咨询,希望希望共同交流心得,一起进步,你我陪伴,学习路上不孤单!
文章目录
前言
回想初次接触STL时,面对庞大知识体系和复杂底层实现的那种茫然感,至今记忆犹新。
最近,十几个小时的钻研,无数次的调试与画图,我终于将网上学习,《STL源码剖析》中vector这一章的精华,结合自己的理解整理成文。
这篇文章记录了我啃源码时的一个个"顿悟"时刻------从最初只把vector当作"会变大的数组",到真正看懂它的迭代器设计、内存管理和性能奥秘。侯捷老师的书让我明白:真正掌握一个工具,必须看到它最底层的模样。
现在我把这份万字长文分享给你。这里有vector的完整解剖图,有每个关键操作背后的源码逻辑,更有从实践中总结的使用技巧。
让我们一起,从使用者变为真正的理解者。
希望这篇文章能成为你深入STL世界的第一步。

1.vector概述
vector的数据安排以及操作方式于array非常相似,二者唯一差别在于对空间的灵活运用。
array是静态空间,一旦配置,不能改变,用户要重新配置空间,移动元素,释放旧空间。vector的内部机制会自行扩充空间。
因此vector给我们带来了很大的灵活性,不用担心空间不够用,而去开一个大array。
vector的技术实现在于对大小的控制 以及数据分配时的移动效率。
引:如果旧空间用满了,每加一个元素就扩容,(配置新空间,挪动数据,释放旧空间),时间成本高,实为不智,所以应有未雨绸缪的考虑。
2.vector的迭代器
vector维护的是一个连续的线性空间,普通的指针可以满足要求,操作行为如:operator*,operator->,operator++,operator--,operator+,operator-,operator+=,operator-=,vector支持随机存取,而普通指针正有这样的能力。
底层代码:
cpp
//vector的迭代器
template <class T, class Alloc = alloc>
class vector {
public:
typedef T value_type;
tyoedef value_type* iterator; //vector的迭代器是普通指针
//...
};
3.vector的数据结构
非常简单,现象连续空间,用两个迭代器start,finish分别指向配置得来的连续空间被已使用的范围,用迭代器end_of_storage指向整块连续空间(含备用空间)的尾端:其实就是最后一个空间的下一个位置。
底层代码:
cpp
template <class T, class Alloc = alloc>
class vector {
//...
protected:
iterator start; //目前使用空间的头
iterator finish; //目前使用空间的尾
iterator end_of_storage; //目前可用空间的尾
};
为降低空间配置速度成本,vector实际配置的大小可能比用户需求更大,以备扩容,这就是容量(capacity),vector的容量大于等于其大小。
运用start,finish,end_of_storage三个迭代器可以容易实现大小,容量,首尾,判断,[], begin(),end()......
底层代码:
cpp
//用start,finish,end_of_storage实现
template <class T, class Alloc = alloc>
class vector {
//...
public:
iterator begin() { return start; }
iterator end() { return finish; }
iterator size() { return size_type(end() - begin()); }
size_type capacity() const { return size_type(end_of_storage - begin()); }
bool empty() const { return begin() == end(); }
reference operator[] (size_type n) { return *(begin() + n); }
reference front() { return *begin(); }
reference back() { return *(end() - 1); }
};

4.vector的构造和内存管理
在gcc编译器下是2倍扩容,MSVC是1.5倍扩容。(扩大,size())
如下:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> iv(2, 9);
cout << "size=" << iv.size() << endl; //size=2
cout << "capacity=" << iv.capacity() << endl; //capacity=2
iv.push_back(1);
cout << "size=" << iv.size() << endl; //size=3
cout << "capacity=" << iv.capacity() << endl; //capacity=4
iv.push_back(2);
cout << "size=" << iv.size() << endl; //size=4
cout << "capacity=" << iv.capacity() << endl; //capacity=4
iv.push_back(3);
cout << "size=" << iv.size() << endl; //size=5
cout << "capacity=" << iv.capacity() << endl; //capacity=8
iv.push_back(4);
cout << "size=" << iv.size() << endl; //size=6
cout << "capacity=" << iv.capacity() << endl; //capacity=8
for (int i = 0; i < iv.size(); ++i)
cout << iv[i] << ' '; //9 9 1 2 3 4
cout << endl;
iv.push_back(5);
cout << "size=" << iv.size() << endl; //size=7
cout << "capacity=" << iv.capacity() << endl; //capacity=8
for (int i = 0; i < iv.size(); ++i)
cout << iv[i] << ' '; //9 9 1 2 3 4 5
cout << endl;
iv.pop_back();
iv.pop_back();
cout << "size=" << iv.size() << endl; //size=5
cout << "capacity=" << iv.capacity() << endl; //capacity=8
iv.pop_back();
cout << "size=" << iv.size() << endl; //size=4
cout << "capacity=" << iv.capacity() << endl; //capacity=8
vector<int>::iterator ivite = find(iv.begin(), iv.end(), 1);
if (ivite != iv.end()) iv.erase(ivite);
cout << "size=" << iv.size() << endl; //size=3
cout << "capacity=" << iv.capacity() << endl; //capacity=8
for (int i = 0; i < iv.size(); ++i)
cout << iv[i] << ' '; //9 9 2
cout << endl;
ivite = find(iv.begin(), iv.end(), 2);
if (ivite != iv.end()) iv.insert(ivite, 3, 7);
cout << "size=" << iv.size() << endl; //size=6
cout << "capacity=" << iv.capacity() << endl; //capacity=8
for (int i = 0; i < iv.size(); ++i)
cout << iv[i] << ' '; //9 9 7 7 7 2
cout << endl;
iv.clear();
cout << "size=" << iv.size() << endl; //size=0
cout << "capacity=" << iv.capacity() << endl; //capacity=8
}
gcc(linux):

MSVC(vs):

vector提供许多constructors:
底层代码:
cpp
//构造函数,允许指定vector的大小和初值
vector(size_type n, const T& value) { fill_initialize(n, value); }
//填充并初始化
void fill_initialize(size_type n, const T& value)
{
start = allocate_and_fill(n, value);
finish = start + n;
end_of_storage = finish;
}
//配置后填充
iterator allocate_and_fill(size_type n, const T& x)
{
iterator result = data_allocator::allocate(n);
uninitialized_fill_n(result, n, x);
return result;
}
push_back()尾插元素时,检查是否还有备用空间,有就在其上插入元素,调整finish,vector变大,没有,就扩容(重新配置,移动数据,释放旧空间)
底层代码:
(篇幅有限,这里只做简略,全局函数具体实现未详细写出)
cpp
//尾插
void push_back(const T& x)
{
if (finish != end_of_storage)
{
construct(finish, x);
++finish;
}
else
insert_aux(end(), x);
}
template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position const T& x)
{
if (finish != end_of_storage)
{
//还有备用空间,在其上构造元素,以最后一个元素为初值
construct(finish, *(finish - 1))
++finish;
T x_copy = x;
copy_bakword(position, finish - 2, finish - 1);
*position = x_copy;
}
else
{
//无备用空间
const size_type old_size = size();
const size_type len = old_size != 0 ? 2 * old_zie : 1; //原大小为0,变为1;不为0,变为2倍;前半段放原数据,后半段放新数据
iterator new_start = data_allocator::allocate(len);
iterator new_finish = new_start;
try
{
new_finish = uninitialized_copy(start, position, new_start);
construct(new_finish, x);
++new_finish;
new_finish = uninitialize_copy(position, finish, new_finish);
}
//...
//析构并释放原vector
destroy(begin(), end());
deallocate();
//调整迭代器,指向新的vector
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;
}
}
动态增加大小,不是在原空间后连续接新空间 ,因为不能保证后面有足够且可分配的空间,以原来两倍的大小配置一块新空间 ,将原来空间的内容拷贝 过来,更新构造元素,释放旧空间。
5.vector的元素操作
pop_back,erase,clear,insert
底层代码:
cpp
//尾删
void pop_back()
{
--finish;
destroy(finish);
}
//清除[first,last)所有元素
iterator erase(iterator first, iterator last)
{
iterator i = copy(last, finish, first);
destroy(i, finish);
finish = finish - (last - first);
return first;
}
//删除某个
iterator erase(iterator position)
{
if (position + 1 != end())
copy(position + 1, finish, position); //全局函数
--finish;
destroy(finish);
return position;
}
void clear() { erase(begin(), end()); }
erase逻辑:

insert逻辑:



6.接口具体使用介绍
6.1.constructor

cpp
#include <vector>
#include <iostream>
using namespace std;
//构造与访问
void test_vector1()
{
vector<int> v1;
vector<int> v2(10, 1);
vector<int> v3(v2.begin(), v2.end());
for (size_t i = 0; i < v3.size(); i++)
{
cout << v3[i] << " "; //用[] 访问
}
cout << endl;
vector<int>::iterator it = v3.begin();
while (it != v3.end())
{
cout << *it << " "; //迭代器
++it;
}
cout << endl;
for (auto e : v3)
{ //范围for,底层就是迭代器
cout << e << " ";
}
cout << endl;
}
int main()
{
test_vector1();
return 0;
}
6.2.resize

n < size(),删除数据,不缩容,n > size(),空间不够,扩容。
不支持头插,头删, 支持尾插,尾删(v.push_bacK(), v.pop_back())
6.3.reserve

要求向量容量至少能够容纳n个元素。
若n大于当前向量容量,该函数将导致容器重新分配存储空间,使其容量增加至n(或更大)。
其他情况,string : n比size()小最多也就缩容到size(),不会影响长度,内容。vector: vs,linux 不缩容
cpp
void test_vector2()
{
//不缩容
vector<int> v(10, 1);
v.reserve(20);
cout << v.size() << endl;
cout << v.capacity() << endl;
v.reserve(20);
cout << v.size() << endl;
cout << v.capacity() << endl;
v.reserve(5);
cout << v.size() << endl;
cout << v.capacity() << endl;
}
6.4.insert

只支持迭代器
v.insert(v.begin(), 1);
v.insert(v.begin() + 3, 2); 3后插入
为了STL统一,因为后面list那些没有下标的概念。
insert,erase本质上要挪动数据,效率低。
6.5.erase

v.erase(v.begin());
v.rease(v.begin() + 1, begin() + 4); 2~5
cpp
void test_vector3()
{
vector<int> v(5, 6);
v.push_back(7);
v.insert(v.begin(), 1);
v.insert(v.begin() + 3, 9);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
v.pop_back();
v.erase(v.begin());
v.erase(v.begin() + 1, v.begin() + 4);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
v.clear()清除所有数据
vector可以比较大小
vector不支持流插入/提取,很灵活。
vector v; (没有\0)不能代替string(有\0,兼容c语言)。string还有很多需求和特性。
vector, vector<vecotr> , (二维数组),还可存自定义类型, 范围for里面用引用,减少拷贝。
cpp
void test_vector4()
{
vector<string> v1;
string s1("xxxx");
v1.push_back(s1);
v1.push_back("yyyyy");
for (const auto& e : v1) //每次都是取得string,&减少拷贝,const不变
{
cout << e << " ";
}
cout << endl;
//二维数组 10 * 5
vector<int> v(5, 1);
vector<vector<int>> vv(10, v);
vv[2][1] = 2; //实际是连续的两个operator[][]的调用
//vv.operator[](2).operator[](1) = 2; 两个operator[]不同
for (size_t i = 0; i < vv.size(); i++)
{
for (size_t j = 0; j < vv[i].size(); ++j)
{
cout << vv[i][j] << " ";
}
cout << endl;
}
cout << endl;
}


加油!志同道合的人会看到同一片风景。
看到这里请点个赞,关注,如果觉得有用就收藏一下吧。后续还会持续更新的。 创作不易,还请多多支持!