目录
一、vector迭代器失效
1.1插入时的迭代器失效
1.2删除时的迭代器失效
二、list迭代器失效
2.1删除时的迭代器失效
在vector与list的模拟实现中,其中有一问题就是迭代器的失效问题,那么迭代器的失效问题具体是指什么,接下来一探究竟。
一、vector迭代器失效
1.1插入时的迭代器失效
在插入元素发生扩容时按原生理解可以用以下图来表示。
但是如果要拷贝的元素里面也开了空间呢?
例如:vector中存的是string对象,那么在进行扩容时,开辟新空间,将原来元素拷贝到新的空间,然而要拷贝的string对象其成员也是开了空间,当我们采用浅拷贝时,发生的就是值拷贝,即string对象的指针拷贝给另一个string对象的指针,那么他们就会指向同一个空间,如下图:
从而当其中一个对象调用析构时,那么其指向的空间就会释放掉,当另一个对象调用析构时,而原来指向的空间已经被上一个对象呢给释放掉了,那么自己在释放已经释放的空间就会造成程序崩溃。这就造成了迭代器的失效
其中memcpy拷贝时发生的就是浅拷贝 :
void reserve(size_t n)
{
if (n > capacity())
{
size_t len = size();
iterator tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, sizeof(T) * (_finish - _start));//浅拷贝
delete[] _start;
}
_start = tmp;
_finish = _start + len;
_end_of_storage = _start + n;
}
}
那么为了解决这个问题,就可以采用赋值的方式,因为赋值他是一种深拷贝,拷贝过去的成员会另外指向一个空间,那么在调用析构时就不会释放已经释放的对象了:
赋值深拷贝:
void reserve(size_t n)
{
if (n > capacity())
{
size_t len = size();
iterator tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * (_finish - _start));//浅拷贝
for (int i = 0; i < len; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + len;
_end_of_storage = _start + n;
}
}
1.2删除时的迭代器失效
对于erase的实现,在删除元素时,删除position位置的元素后,返回的还是当前position的迭代器,只不过position的迭代器不在指向原来的空间了,导致原来的迭代器就失效了,如下:
iterator erase(iterator position)
{
assert(position < size());
if (size())
{
size_t len = _finish - position - 1;
for (int i = 0; i < len; i++)
{
*(position + i) = *(position + i + 1);
}
--_finish;
}
return position;
}
通过一张图来描绘:
通过例子来解释上述现象:
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
auto it = v.begin();
while (it != v.end())
{
if (*it == 3)
it = v.erase(it);//删除迭代器位置指向的3,返回迭代器位置,此时迭代器指向的是4
else
cout << *it << " ";
it++;//这里再++,那么迭代器就指向了5去了
}
return 0;
}
二、list迭代器失效
对于list而言,其底层是连续的,那么插入元素时,不存在需要扩容的情况, 只是创建节点进行链接即可。但是,对于删除元素时还是跟vector一样存在迭代器的失效。
2.1删除时的迭代器失效
iterator erase(iterator pos)
{
assert(pos != end());
pNode cur = pos._node;
pNode next = cur->_next;
cur->_prev->_next = next;
next->_prev = cur->_prev;
delete cur;
return next;
}
对于list的erase的实现,在删除元素时,删除pos位置的元素后,返回的还是当前pos的迭代器 ,只不过pos不在指向原来的空间了,导致原来的的迭代器就失效了,如下图:
通过例子来解释上述现象:
int main()
{
list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
auto it = l.begin();
while (it != l.end())
{
if (*it == 2)
it = l.erase(it);//删除迭代器位置指向的2,返回迭代器位置,此时迭代器指向的是3
else
cout << *it << " ";
it++;//这里再++,那么迭代器就指向了4去了
}
return 0;
}
对于以上迭代器的失效问题,总结就一句话,最好不要去使用已经删除过的迭代器