您的位置:首页 > 娱乐 > 八卦 > STL_vector实现及干货以及vector迭代器失效问题

STL_vector实现及干货以及vector迭代器失效问题

2025/1/15 13:00:03 来源:https://blog.csdn.net/2301_79127392/article/details/142285003  浏览:    关键词:STL_vector实现及干货以及vector迭代器失效问题

vector迭代器失效:结果未定义

场景1:insert

你想在pos位置插入一个值

typedef T* iterator;
void insert(iterator pos,const T& val)
{assert(pos>=_start);assert(pos<=_finish);//空间不够扩容if(_finish=_endofstorage){reverse(capacity()==0?4:capacity()*2);}iterator cur=_finish;while(cur>=pos){*cur=*(cur-1);cur--;}*cur=T(val);_finish++;
}

如果你用上面的函数用的次数多了,当它扩容的时候,你去调试,会发现pos位置变成随机值了,因为扩容可能会遇到异地扩容,当异地扩容的时候,原本的空间会被释放,把数据挪移到另外的空间,正确的写法应该是下面这种

typedef T* iterator;
//返回插入数据的位置
iterator insert(iterator pos, const T& val)
{assert(pos >= _start && pos <= _finish);if (size() == capacity()){size_t n = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + n;}iterator cur = _finish;while (cur != pos){*cur = *(cur - 1);cur--;}*pos = val;_finish++;return pos;
}

场景2:erase

void erase(iterator pos)
{assert(pos>=_start&&pos<=_finish);iterator cur=pos+1;while(cur<_finish){*(cur-1)=*cur;cur++;}_finish--;
}

由于pos位置删除完了以后,数据会发生改变,或者pos位置直接会没有有效数据,比如说尾删,删除了以后pos位置的数据非法,一般来说,我们不会再去用删除数据后的pos,但有一个场景下会出事,比如说连续删除

//假设你要删除一个vector里所有的偶数
int main()
{vector<int> vec;iterator it=vec.begin();while(it!=vec.end()){if(*it%2==0){erase(it);}++it;}return 0;
}

如果你真的这样写了,你会发现偶数可能删不干净,甚至出现越界的问题,所以我们一般认为只要删除,迭代器就失效,但官方的stl库给出了一个解决方法,如下图,大概意思是当erase删除后,会返回删除数据的下一个位置
在这里插入图片描述

typedef T* iterator;
iterator erase(iterator pos)
{assert(size() != 0);assert(pos >= _start && pos <= _finish);iterator cur = pos + 1;while (cur != _finish){*(cur - 1) = *cur;cur++;}_finish--;return pos;
}

vector的六个成员函数

构造函数

无参构造函数

刚开始如果没说有数据的话,我们一般不开空间。

vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{}

用n个val构造

vector(size_t n, const T& val = T())
{_start = new T[n];_finish = _start + n;_endofstorage = _start + n;iterator cur = _start;while (cur != _endofstorage){*cur = val;cur++;}
}

我们一般会重载一个下面的版本,因为如果vector遇到整型,而我们又有区间构造和n个val构造的函数,编译器会优先选择区间构造,因为无符号整型跟你传的整型值没有说非常匹配,所以我们可以把size_t传参类型改成int类型重载一份,这样编译器遇到整型构造的时候就不会去选择区间构造了。

vector(int n, const T& val = T())
{_start = new T[n];_finish = _start + n;_endofstorage = _start + n;iterator cur = _start;while (cur != _endofstorage){*cur = val;cur++;}
}

区间构造

一定要用模板,因为你不知道需要传的区间的类型

template<class InputIterator>
vector(InputIterator begin, InputIterator end):_start(nullptr),_finish(nullptr),_endofstorage(nullptr)
{while (begin != end){push_back(*begin);begin++;}
}

拷贝构造

注意,在扩容的时候,一定不能用memcpy或者memmove这种函数,因为这种函数是逐字节逐字节拷贝的,会发生析构两次或者写其中一个变量导致另外一个变量变化的问题,在这边为了解决这个问题,我们可以用赋值的方法,如果有自定义类型,它会自动去调用自定义类型的赋值重载函数。

vector(const vector<T>& v)
{_start = new T[v.capacity()];_finish = _start + v.size();_endofstorage = _start + v.capacity();iterator cur = _start;for (size_t i = 0; i < v.size(); i++){*cur = v[i];cur++;}
}

赋值重载

下面的重载=的函数传参的时候是有调用拷贝构造产生新的vector的,我们为了可以减少程序资源消耗,可以直接把两个变量的指针交换了,可能有的人会问,这个传参是临时变量啊,出了重载函数不就会销毁了吗,但其实这个变量是new出来的,我们只要不手动释放,就会一直留在那边,编译器不会帮我们释放。

void swap(vector<T> v)
{_start = v._start;_finish = v._finish;_endofstorage = v._endofstorage;
}
vector<T>& operator=(const vector<T> v)
{swap(v);
}

析构函数

三个iterator变量最好全部都置空一下,保持良好的写代码习惯

~vector()
{delete[] _start;_start = _finish = _endofstorage = nullptr;
}

取地址操作符重载函数

这两个函数一般不会自己实现的,都是由编译器自行产生的

vector<T>* operator&()
{return this;
}
const vector<T>* operator&() const
{return this;
}

增删查改

push_back可以复用insert

iterator insert(iterator pos, const T& val)
{assert(pos >= _start && pos <= _finish);if (size() == capacity()){size_t n = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + n;}iterator cur = _finish;while (cur != pos){*cur = *(cur - 1);cur--;}*pos = val;_finish++;return pos;
}
void push_back(const T& val)
{/*if (size() == capacity()){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = val;_finish++;*/insert(end(), val);
}

和增同理,pop_back同样可以复用erase

iterator erase(iterator pos)
{assert(size() != 0);assert(pos >= _start && pos <= _finish);iterator cur = pos + 1;while (cur != _finish){*(cur - 1) = *cur;cur++;}_finish--;return pos;
}
void pop_back()
{assert(size() != 0);//_finish--;erase(end() - 1);
}

至于查和改,就是一个遍历而已,这边不再进行实现

capacity()和size()

返回空间大小和实际使用空间的大小

size_t capacity()
{size_t cp = _endofstorage - _start;return cp;
}
size_t capacity() const
{size_t cp = _endofstorage - _start;return cp;
}
size_t size()
{size_t sz = _finish - _start;return sz;
}
size_t size() const
{size_t sz = _finish - _start;return sz;
}

reserve和resize

这边要记得,同样不能使用memmove或者memcpy这种函数,容易造成浅拷贝问题,应该要遍历一遍原空间,然后一个一个赋值给新空间,这样才能实现深拷贝

void reserve(size_t n)
{if (n > capacity()){iterator tmp = new T[n];iterator cur1 = _start;iterator cur2 = tmp;size_t cp = _finish - _start;while (cur1 != _finish){*cur2 = *cur1;cur2++;cur1++;}delete[] _start;_start = tmp;_finish = _start + cp;_endofstorage = _start + n;}
}
void resize(size_t n, const T& val = T())
{if (n <= size()){_finish = _start + n;}else{if (n > capacity()){reserve(n);}iterator tmp = _finish;while (tmp != _start + n){*tmp = val;tmp++;}}
}

assess

这边要重载两种,不然const对象无法使用[ ]

vector<T>& operator=(const vector<T> v)
{swap(v);
}
T& operator[](size_t index)
{return *(_start + index);
}

vector的迭代器

这边vector的迭代器跟string一样,用的是原生指针

typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{return _start;
}
iterator end()
{return _finish;
}
const_iterator cbegin() const
{return _start;
}
const_iterator cend() const
{return _finish;
}

vector实现的完整代码

#include<string>
#include<assert.h>
using namespace std;
namespace vientiane
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator cbegin() const{return _start;}const_iterator cend() const{return _finish;}//construct and destroyvector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}//用n个值构造vector(size_t n, const T& val = T()){_start = new T[n];_finish = _start + n;_endofstorage = _start + n;iterator cur = _start;while (cur != _endofstorage){*cur = val;cur++;}}vector(int n, const T& val = T()){_start = new T[n];_finish = _start + n;_endofstorage = _start + n;iterator cur = _start;while (cur != _endofstorage){*cur = val;cur++;}}template<class InputIterator>vector(InputIterator begin, InputIterator end):_start(nullptr),_finish(nullptr),_endofstorage(nullptr){while (begin != end){push_back(*begin);begin++;}}vector(const vector<T>& v){_start = new T[v.capacity()];_finish = _start + v.size();_endofstorage = _start + v.capacity();iterator cur = _start;for (size_t i = 0; i < v.size(); i++){*cur = v[i];cur++;}}void swap(vector<T> v){_start = v._start;_finish = v._finish;_endofstorage = v._endofstorage;}vector<T>& operator=(const vector<T> v){swap(v);}T& operator[](size_t index){return *(_start + index);}const T& operator[](size_t index) const{return *(_start + index);}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}size_t capacity(){size_t cp = _endofstorage - _start;return cp;}size_t capacity() const{size_t cp = _endofstorage - _start;return cp;}size_t size(){size_t sz = _finish - _start;return sz;}size_t size() const{size_t sz = _finish - _start;return sz;}void reserve(size_t n){if (n > capacity()){iterator tmp = new T[n];iterator cur1 = _start;iterator cur2 = tmp;size_t cp = _finish - _start;while (cur1 != _finish){*cur2 = *cur1;cur2++;cur1++;}delete[] _start;_start = tmp;_finish = _start + cp;_endofstorage = _start + n;}}void resize(size_t n, const T& val = T()){if (n <= size()){_finish = _start + n;}else{if (n > capacity()){reserve(n);}iterator tmp = _finish;while (tmp != _start + n){*tmp = val;tmp++;}}}//modifyvoid push_back(const T& val){/*if (size() == capacity()){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = val;_finish++;*/insert(end(), val);}void pop_back(){assert(size() != 0);//_finish--;erase(end() - 1);}//返回插入数据的位置iterator insert(iterator pos, const T& val){assert(pos >= _start && pos <= _finish);if (size() == capacity()){size_t n = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + n;}iterator cur = _finish;while (cur != pos){*cur = *(cur - 1);cur--;}*pos = val;_finish++;return pos;}//返回删除数据位置的下一个位置iterator erase(iterator pos){assert(size() != 0);assert(pos >= _start && pos <= _finish);iterator cur = pos + 1;while (cur != _finish){*(cur - 1) = *cur;cur++;}_finish--;return pos;}private:iterator _start;iterator _finish;iterator _endofstorage;};
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com