深入解剖STL Vector:从底层原理到核心接口的灵活运用
💗博主介绍:计算机专业的一枚大学生 来自重庆 @燃于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支持随机存取,而普通指针正有这样的能力。
底层代码:
//vector的迭代器template<classT,classAlloc= alloc>classvector{public:typedef T value_type; tyoedef value_type* iterator;//vector的迭代器是普通指针//...};3.vector的数据结构
非常简单,现象连续空间,用两个迭代器start,finish分别指向配置得来的连续空间被已使用的范围,用迭代器end_of_storage指向整块连续空间(含备用空间)的尾端:其实就是最后一个空间的下一个位置。
底层代码:
template<classT,classAlloc= alloc>classvector{//...protected: iterator start;//目前使用空间的头 iterator finish;//目前使用空间的尾 iterator end_of_storage;//目前可用空间的尾};为降低空间配置速度成本,vector实际配置的大小可能比用户需求更大,以备扩容,这就是容量(capacity),vector的容量大于等于其大小。
运用start,finish,end_of_storage三个迭代器可以容易实现大小,容量,首尾,判断,[], begin(),end()……
底层代码:
//用start,finish,end_of_storage实现template<classT,classAlloc= alloc>classvector{//...public: iterator begin(){return start;} iterator end(){return finish;} iterator size(){returnsize_type(end()-begin());} size_type capacity()const{returnsize_type(end_of_storage -begin());}boolempty()const{returnbegin()==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())
如下:
#include<iostream>#include<vector>#include<algorithm>usingnamespace std;intmain(){ 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=8for(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=8for(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=8for(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=8for(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:
底层代码:
//构造函数,允许指定vector的大小和初值vector(size_type n,const T& value){fill_initialize(n, value);}//填充并初始化voidfill_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变大,没有,就扩容(重新配置,移动数据,释放旧空间)
底层代码:
(篇幅有限,这里只做简略,全局函数具体实现未详细写出)
//尾插voidpush_back(const T& x){if(finish != end_of_storage){construct(finish, x);++finish;}elseinsert_aux(end(), x);}template<classT,classAlloc>voidvector<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);}//...//析构并释放原vectordestroy(begin(),end());deallocate();//调整迭代器,指向新的vector start = new_start; finish = new_finish; end_of_storage = new_start + len;}}动态增加大小,不是在原空间后连续接新空间,因为不能保证后面有足够且可分配的空间,以原来两倍的大小配置一块新空间,将原来空间的内容拷贝过来,更新构造元素,释放旧空间。
5.vector的元素操作
pop_back,erase,clear,insert
底层代码:
//尾删voidpop_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;}voidclear(){erase(begin(),end());}erase逻辑:
insert逻辑:
6.接口具体使用介绍
6.1.constructor
#include<vector>#include<iostream>usingnamespace std;//构造与访问voidtest_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;}intmain(){test_vector1();return0;}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 不缩容
voidtest_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
voidtest_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里面用引用,减少拷贝。
voidtest_vector4(){ vector<string> v1; string s1("xxxx"); v1.push_back(s1); v1.push_back("yyyyy");for(constauto& 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;}加油!志同道合的人会看到同一片风景。
看到这里请点个赞,关注,如果觉得有用就收藏一下吧。后续还会持续更新的。 创作不易,还请多多支持!