vector迭代器失效:结果未定义
场景1:insert
你想在pos位置插入一个值
cpp
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位置变成随机值了,因为扩容可能会遇到异地扩容,当异地扩容的时候,原本的空间会被释放,把数据挪移到另外的空间,正确的写法应该是下面这种
cpp
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
cpp
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,但有一个场景下会出事,比如说连续删除
cpp
//假设你要删除一个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删除后,会返回删除数据的下一个位置
cpp
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的六个成员函数
构造函数
无参构造函数
刚开始如果没说有数据的话,我们一般不开空间。
cpp
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
用n个val构造
cpp
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类型重载一份,这样编译器遇到整型构造的时候就不会去选择区间构造了。
cpp
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++;
}
}
区间构造
一定要用模板,因为你不知道需要传的区间的类型
cpp
template<class InputIterator>
vector(InputIterator begin, InputIterator end)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
while (begin != end)
{
push_back(*begin);
begin++;
}
}
拷贝构造
注意,在扩容的时候,一定不能用memcpy或者memmove这种函数,因为这种函数是逐字节逐字节拷贝的,会发生析构两次或者写其中一个变量导致另外一个变量变化的问题,在这边为了解决这个问题,我们可以用赋值的方法,如果有自定义类型,它会自动去调用自定义类型的赋值重载函数。
cpp
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出来的,我们只要不手动释放,就会一直留在那边,编译器不会帮我们释放。
cpp
void swap(vector<T> v)
{
_start = v._start;
_finish = v._finish;
_endofstorage = v._endofstorage;
}
vector<T>& operator=(const vector<T> v)
{
swap(v);
}
析构函数
三个iterator变量最好全部都置空一下,保持良好的写代码习惯
cpp
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
取地址操作符重载函数
这两个函数一般不会自己实现的,都是由编译器自行产生的
cpp
vector<T>* operator&()
{
return this;
}
const vector<T>* operator&() const
{
return this;
}
增删查改
增
push_back可以复用insert
cpp
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
cpp
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()
返回空间大小和实际使用空间的大小
cpp
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这种函数,容易造成浅拷贝问题,应该要遍历一遍原空间,然后一个一个赋值给新空间,这样才能实现深拷贝
cpp
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对象无法使用[ ]
cpp
vector<T>& operator=(const vector<T> v)
{
swap(v);
}
T& operator[](size_t index)
{
return *(_start + index);
}
vector的迭代器
这边vector的迭代器跟string一样,用的是原生指针
cpp
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实现的完整代码
cpp
#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 destroy
vector()
:_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++;
}
}
}
//modify
void 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;
};
}