vector中存在的迭代器失效问题

vector中存在的迭代器失效问题

vector的基本轮廓是这样的:

template<class T>
class vector
{
public:
    typedef T* iterator;
    typedef const T* const_iterator;
    vector()
        :_start(nullptr)
            , _finish(nullptr)
            , _endofstoage(nullptr)
        {}
private:
    iterator _start;
    iterator _finish;
    iterator _endofstorage;
};

就像这样:

image-20221209142841956

insert存在的迭代器失效

此时我们实现一个插入操作:

老生常谈的,插入前判断如果满了就扩容,先来一个扩容函数reserve:

void reserve(size_t n)
{
    size_t sz = size();
    if (n > capacity())
    {
        T* tmp = new T[n];
        if (_start)
        {
            memcpy(tmp, _start, size()*sizeof(T));
            delete[] _start;
        }

        _start = tmp;
        _finish = _start + sz;
        _endofstoage = _start + n;
    }
}
void insert(iterator pos,const T& x)
{
	assert(pos>=_start && pos<=_finish);//判断合法性
    
    //容量问题
    if(_start==_endofstorage)
    {
        size_t newcapacity=capacity()==0?4:capacity()*2;
        reserve(newcapacity);
    }
    //挪动数据
	iterator end=_finish-1;
    while(end>=pos)
    {
        *(end+1)=*(end);
        --end;
    }
    //插入数据
    *pos=x;
    ++_finish;
}

以上实现的insert看似好像没什么问题,但是其实是存在一些隐患的。

如果我们没有发生扩容操作,自然没有什么问题,可以顺利的插入进去。

但是如果空间不足发生了扩容操作,这会问题就出现了:

我们可以看到调用reserve函数,会将_ start和_finish更新,但是我们利用的pos没有被更新pos还指向旧空间,就会失效无法与新的end交互,变成一个野指针,

这就是经典的迭代器失效问题。

那我们应该怎么修改呢:

void insert(iterator pos, const T& x)
{
    // 检查参数
    assert(pos >= _start && pos <= _finish);

    // 扩容
    // 扩容以后pos就失效了,需要更新一下
    if (_finish == _endofstoage)
    {
        size_t n = pos - _start;//先把这个距离保存下来

        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);

        pos = _start + n;//此时更新pos
    }

    // 挪动数据
    iterator end = _finish - 1;
    while (end >= pos)
    {
        *(end + 1) = *end;
        --end;
    }

    *pos = x;
    ++_finish;
}

看似问题好像解决了?

此时,我们再进行一个操作:

在所有的偶数前面插入2

vector<int>::iterator it=v.begin();
while(it!=v.end())
{
   if(*it%2==0)
   {
        v.insert(it,20);
   }
    ++it;
}

结果就是发现会报断言错误,原因是:

insert之后虽然没有扩容,it没有变成野指针,但是it指向的位置意义变了,应该指向插入后最新的那个位置,因此我们需要重新拿到新位置重新处理,这个问题我们利用返回值来解决

iterator insert(iterator pos, const T& x)
{
    // 检查参数
    assert(pos >= _start && pos <= _finish);

    // 扩容
    //更新一下
    if (_finish == _endofstoage)
    {
        size_t n = pos - _start;

        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);

        pos = _start + n;
    }

    // 挪动数据
    iterator end = _finish - 1;
    while (end >= pos)
    {
        *(end + 1) = *end;
        --end;
    }

    *pos = x;
    ++_finish;
    return pos;
}

此时上面那个插入的问题也应该这样得到解决:

vector<int>::iterator it=v.begin();
while(it!=v.end())
{
   if(*it%2==0)
   {
        v.insert(it,20);
       ++it;
   }
    ++it;
}

erase存在的迭代器失效问题

首先我们要明确的是,我们在删除的时候一般是不考虑缩容的情况的,即空间位置并没有发生改变,那么erase如果出现迭代器失效,一般也不会是野指针的问题。

因此erase的失效基本都是因为意义变了,或者不在有效访问数据范围内。

平台的差异性

这样看来其实迭代器失效大体分为两类:

1、扩容缩容引起的野指针引起的迭代器失效问题。

2、迭代器指向的位置变了。

当我们在Windows VS下进行测试与在Linux g++下测试的时候,结果是不一样的。

对于insert和erase造成的迭代器失效问题,Linux平台的检查的相当佛性,基本都是靠操作系统自身野指针越界机制去检查(不一定检查得到)。但是Windows VS下则更为严格,运用了一些强制检查机制,基本都会检查出来。

但是!

无论在哪个平台下,我们都要心知肚明迭代器失效的这些问题,在使用的时候一定要做到如履薄冰。明确了这里会存在迭代器失效问题,用起来才会更加的得心应手。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值