文章目录
- 前言
- 成员变量
- 成员函数
- vector ()
- size_t size()
- size_t capacity()
- iterator begin()和const_iterator begin()const
- iterator end()和const_iterator end()const
- ~vector()
- void push_back(const&T val)
- vector<T>(const vector<T>& v)
- vector<T>& operator=(vector<T> v)
- void swap(vector<T> &v)
- void reserve(size_t n)
- void resize(size_t n,const T&val=T( ))
- T& front() 和const T& front()const
- T& back()和const T& back() const
- void pop_back()
- iterator insert(iterator pos, const T& val)
- void erase(iterator pos)
- vector(InputIterator first, InputIterator last)
- 完整代码
- 总结
前言
在上篇文章中,我们介绍了vector的使用,在本篇文章中我们将会详细介绍一下vector的底层逻辑,我们会对vector有更深层次的理解
成员变量
在vector中,我们可以是任意类型,所以我们要采用灵活的方式。在C语言中,我们采用typedef的方式改变存储类型。在C++中进入了模版的概念,我们采用模版来实现。
在C语言板块,我们采用三个变量记录vector的变化,分别是int size,int capacity,int *a;
我们如果去查看vector的底层逻辑,我们就会发现,vector底层采用三个指针记录数据以及空间的变化,指针用迭代器的方式实现
template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;private://内置成员变量声明给缺省值T* _start=nullptr;T* _finish=nullptr;T* _endofstorage=nullptr;};
我们来简单介绍一下这几个指针:
_start是vector中元素初始位置
_finish是vector中元素的结束位置
_endofstorage是vector中的容量大小
侯捷老师《STL原码剖析》这本书画图的,书上的展示图如下:
成员函数
vector ()
我们在三个成员定义时已经进行了初始化操作
size_t size()
这个函数是用来计算vector中数据的大小
size_t size(){return _finish - _start;}
size_t capacity()
这个函数是用来计算vector中容量的大小
size_t capacity(){return _endofstorage - _start;}
iterator begin()和const_iterator begin()const
这个函数用来记录第一个元素的位置
iterator begin(){return _start;}const_iterator begin()const{return _start;}
iterator end()和const_iterator end()const
这个函数用来记录最后一个元素的下一个位置
iterator end(){return _finish;}const_iterator end()const{return _finish;}
~vector()
这个函数用来对空间进行清理
~vector(){if (_start)//判断是否有资源需要清理{delete[]_start;_start = _finish = _endofstorage = nullptr;}}
void push_back(const&T val)
这个函数是用来在尾部插入数据
void push_back(const T& val)
{//首先判断是否有空间,进行插入数据if (_finish == _endofstorage){//空间不足,需要开空间,提前计算好需要多大的空间size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;//我们一般采用reserve函数进行开空间//因为这个函数是专门用来进行对空间的开辟的reserve(newcapacity);}*_finish = val;_finish++;
}
vector(const vector& v)
拷贝构造
vector<T>(const vector<T>& v){//开空间+尾插reserve(v.capacity());for (auto& e : v){push_back(e);}}
vector& operator=(vector v)
赋值功能
我们可以开辟一块空间,在一个个把值拷贝过去,再释放不用的空间,下面我们采取一种灵活的方式
我们已tmp[i]=_start[i]为例
vector<T>& operator=(vector<T> v){swap(v);return *this;}
vector v这个地方不能加引用!!!
我们来看一下具体的实现
🌟 在调用之前,会发生拷贝,拷贝一份相同的内容
🌟 把v和tmp进行交换,指针指向发生变化
🌟 v是局部变量,出了作用域进行销毁,完成深拷贝工作
我们不开空间而是直接构造
void swap(vector &v)
交换两个vector
void swap(vector<T> &v){//仅仅通过交换两个指针的位置就可以解决 //必须指定是库里的,要不然就会发生死递归,最终栈溢出std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}
void reserve(size_t n)
这个函数是用来对空间进行开辟的,开n个大小的空间
void reserve(size_t n)
{//判断是否需要开空间if (n > capacity()){//开空间T* tmp = new T[n];if (_start){//拷贝之前的数据到新空间memcpy(tmp, _start, sizeof(T) * n);//释放旧空间delete[]_start;}//更新指针_start = tmp;_finish = _start +size();_endofstorage = _start + capacity();}
}
我们来测试一下下面这段代码
void test1(){vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (auto& e : v){cout << e << " ";}cout << endl;}
我们会发现,直接报错了,经过调试我们发现了错误所在
我们来画图看一看
我们发现,再对指针_finish更新时,不能用size(),因为之前的_start已经进行了更新,计算不到之前的数据个数了。
我们需要在进行更新指针之前,对这个size()进行备份,对于_endofstorage,容量也发生来更新,不能用原来的
void reserve(size_t n){//判断是否需要开空间if (n > capacity()){//提前记录数据个数size_t old = size();//开空间T* tmp = new T[n];//拷贝之前的数据到新空间memcpy(tmp, _start, sizeof(T) * old);//更新指针_start = tmp;_finish = _start + old;_endofstorage = _start + n;}}
我们这里还有问题需要处理,memcpy拷贝呢是一个字节一个字节的拷贝,属于浅拷贝。
我们画个图来看一下
浅拷贝对于没有资源的类型是没有问题的,但是对于有资源的成员变量就有问题了,开辟新空间,拷贝数据,两块空间的内容都指向同一块空间,旧空间需要被释放,指向的那块空间就被释放掉了,新空间就没法玩了。
我们在处理vector中挪动数据的问题是不能用memcpy进行浅拷贝,我们应该开一块同一样的空间,把数据插入进去。
我们这里采取一种全新的方法
void reserve(size_t n){//判断是否需要开空间if (n > capacity()){size_t old = size();//开空间t* tmp = new t[n];if (_start){//拷贝之前的数据到新空间//memcpy(tmp, _start, sizeof(t) * n);for (int i = 0; i < size(); i++){tmp[i] = _start[i];}//释放旧空间delete[]_start;}//更新指针_start = tmp;_finish = _start + old;_endofstorage = _start + n;}
tmp[i] = _start[i];就可以完成我们的任务。
void resize(size_t n,const T&val=T( ))
这个函数用于对空间开辟+初始化
我们来看const T&val=T( )这个,这个其实就是缺省值
对于自定义类型会调用它的默认构造,那麽对于内置类型也要调用它的默认构造,这个是为了适应模板才加入的。
比如int默认为0,char默认为’/0‘,double默认为0.0等等
对于resize的大小,有三种情况:
🌟 size()<n<capacity,我们仅需要插入数据即可
🌟 size()>capacity(),我们先需要开空间,再进行数据的插入
🌟 size()>n,我们需要对数据进行删除
我们可以把前两种情况合并到一起,直接开空间
void resize(size_t n,const T&val=T( ))
{//需要开空间if (n > capacity()){size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);//插入数据while (_finish < _start + n){*_finish = val;*_finish++;}}else{_finish = _start + n;}}
T& front() 和const T& front()const
获取vector中第一个元素
T& front(){return *_start;}const T& front()const{return *_start;}
T& back()和const T& back() const
获取vector中最后有一个元素
T& back(){return *(_finish - 1);}const T& back() const{return *(_finish - 1);}
void pop_back()
删除最后一个元素
void pop_back(){assert(size() > 0);_finish--;}
iterator insert(iterator pos, const T& val)
在pos位置插入数据val
void insert(iterator pos, const T& val){//进行断言assert(pos >= _start);assert(pos <= _finish);//先判断扩容if (_endofstorage == _finish){int len = pos - _start;size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);pos = _start + len;}//挪动数据,必须从后往前挪iterator end = _finish-1;while (end >= pos){*(end + 1) = *end;end--;}//插入数据*pos = val;_finish++;}
我们来关注一下这段代码的实现
如果不加上的话程序会产生错误,就会发生迭代器失效
当程序发生扩容,原来的空间会进行销毁,vector中三个元素都会发生变化,而pos所处的位置不变,pos的那块空间被销毁,以后进行插入进找不到了。
在解决这类问题时:我们仅仅需要记录一下pos的位置就可以完成操作。
iterator insert(iterator pos, const T & val){assert(pos >= _start);assert(pos <= _finish);//先判断扩容if (_endofstorage == _finish){int len = pos - _start;size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);pos = _start + len;return pos;}//挪动数据,必须从后往前挪iterator end = _finish-1;while (end >= pos){*(end + 1) = *end;end--;}//插入数据*pos = val;_finish++;}
void erase(iterator pos)
删除pos位置的元素,erase也可能会引发迭代器失效
void erase(iterator pos){//断言检查assert(pos >= _start);assert(pos < _finish);//有、数据移动iterator it = pos + 1;while (it < _finish){*(it+1) = *it;it++;}//删除数据_finish--;}
我们来看一下这段代码
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);auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)v.erase(it);else {++it;}}}
这段代码其实是有问题的,如果erase发生缩容,pos是一个迭代器,位置就会发生变化,
有的编译器下size()减小就会发生缩容,那么空间就会发生变化,外边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(InputIterator first, InputIterator last)
一段迭代器区间初始化
template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}
指向连续物理空间的指针就是天然迭代器
int a[] = { 100, 200, 300 };vector<int> v4(a, a+3);for (auto e : v4){cout << e << " ";}cout << endl;
完整代码
模板不能声明和定义在同一个文件中
namespace peng
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//初始化列表初始化vector(){ }size_t size(){return _finish - _start;}size_t capacity(){return _endofstorage - _start;}iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin()const{return _start;}const_iterator end()const{return _finish;}~vector(){if (_start){delete[]_start;_start = _finish = _endofstorage = nullptr;}}//拷贝构造vector<T>(const vector<T>& v){//开空间+尾插reserve(capacity());for (auto& e : v){push_back(e);}}//常见错误//void reserve(size_t n)//{// //判断是否需要开空间// if (n > capacity())// {// //开空间// T* tmp = new T[n];// if (_start)// {// //拷贝之前的数据到新空间// memcpy(tmp, _start, sizeof(T) * n);// //释放旧空间// delete[]_start;// }// //更新指针// _start = tmp;// _finish = _start +size();// _endofstorage = _start + capacity();// }//}//void reserve(size_t n)//{// //判断是否需要开空间// if (n > capacity())// {// size_t old = size();// //开空间// T* tmp = new T[n];// if (_start)// {// //拷贝之前的数据到新空间// memcpy(tmp, _start, sizeof(T) * n);// //释放旧空间// delete[]_start;// }// //更新指针// _start = tmp;// _finish = _start + old;// _endofstorage = _start + n;// }//}void reserve(size_t n){//判断是否需要开空间if (n > capacity()){size_t old = size();//开空间T* tmp = new T[n];if (_start){//拷贝之前的数据到新空间//memcpy(tmp, _start, sizeof(t) * n);for (size_t i = 0; i < size(); i++){tmp[i] = _start[i];}//释放旧空间delete[]_start;}//更新指针_start = tmp;_finish = _start + old;_endofstorage = _start + n;}}void push_back(const T & val){//插入之前判断扩容if (_finish == _endofstorage){//计算开多大空间size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);}//插入数据*_finish = val;_finish++;}///* vector<T>& operator=(vector<T> v)// {// swap(v);// return *this;// }*/T& operator[](size_t pos){return _start[pos];}const T& operator[](size_t pos)const{return _start[pos];}void resize(size_t n, const T & val = T()){//需要开空间if (n > size()){size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);//插入数据while (_finish < _start + n){*_finish = val;*_finish++;}}else{_finish = _start + n;}}T& front(){return *_start;}T& back(){return *(_finish - 1);}const T& front()const{return *_start;}const T& back() const{return *(_finish - 1);}void pop_back(){assert(size() > 0);_finish--;}iterator insert(iterator pos, const T & val){assert(pos >= _start);assert(pos <= _finish);//先判断扩容if (_endofstorage == _finish){int len = pos - _start;size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);pos = _start + len;return pos;}//挪动数据,必须从后往前挪iterator end = _finish-1;while (end >= pos){*(end + 1) = *end;end--;}//插入数据*pos = val;_finish++;}vector<T>& operator=(vector<T> v){swap(v);return *this;}//vector<T>& operator=(vector<T> v)//{// //开空间+数据插入// reserve(capacity());// _start = new T[v.capacity()]; //这里要开辟容量的空间大小// _finish = _start + v.size(); //指定里面存到的数据位置// _endofstorage = _start + v.capacity();// memcpy(_start, v._start, v.size() * sizeof(T)); //注意这里有一个Bug// return *this;//}void swap(vector<T> &v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}//void erase(iterator pos)//{// assert(pos >= _start);// assert(pos < _finish);// iterator it = pos + 1;// while (it < _finish)// {// *it = *(it + 1);// it++;// }// _finish--;//}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish){*(it-1) = *it;it++;}_finish--;return pos;}//template<class InputIterator>//vector(InputIterator first, InputIterator last)//{// while (first != last)// {// push_back(*first);// ++first;// }//}vector(size_t n, const T& value = T()){resize(n,value);}private:T* _start = nullptr;T* _finish = nullptr;T* _endofstorage = nullptr;};}
总结
以上就是今天要讲的内容,本文仅仅详细介绍了C++vector的模拟实现,希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘