vector的迭代器失效问题&深浅拷贝
- 一,vector迭代器失效问题
- 1. 什么是迭代器失效
- 2. insert()导致的迭代器失效问题
- 3. 解决办法
- 4. erase() 导致的迭代器失效问题
- 5. 解决办法
- 二,深浅拷贝问题
- 1. 什么是深浅拷贝问题
- 2.reserve的深度解析
- 3.解决办法
一,vector迭代器失效问题
1. 什么是迭代器失效
先说一下什么是迭代器失效
迭代器失效本质上来讲,就是在插入和删除后pos所指的位置不是更新后的位置,而是原来的位置。
2. insert()导致的迭代器失效问题
insert可能会导致迭代器失效,这是因为当插入数据需要扩容时,返回的pos位置还是原来的那块地址,但是扩容后插入的位置已经发生了变化,所以会导致迭代器失效。
3. 解决办法
为了解决这种问题,所以vector的insert在其完成插入操作后会返回其更新后的迭代器位置。
4. erase() 导致的迭代器失效问题
erase也可能会导致迭代器失效
看下面的场景:
如果要删除vector中每个偶数,
vector<int> v{1,2,2,3,4,5,6,7};
vector<int>::iterator it = v.begin();
while(it != v.end()){if(*it % 2 == 0){it = v.erase(it);}++it;
}
这里会出现第二个2没有被删除的情况,原因如下:
5. 解决办法
要解决这样的问题,我们可以让 it++ 只在不是偶数的时候操作
vector<int> v{1,2,2,3,4,5,6,7};
vector<int>::iterator it = v.begin();
while(it != v.end()){if(*it % 2 == 0){it = v.erase(it);}else{++it; }}
二,深浅拷贝问题
1. 什么是深浅拷贝问题
假设有这样一个场景,当vector要发生拷贝构造时,如果我们按照常规的写法来写拷贝构造:
myvector(const myvector<T>& v) {//拷贝构造_start = new T[v.capacity()];memcpy(_start, v._start, sizeof(T) * v.size());_finish = _start + v.size();_end_of_storage = _start + v.capacity();
}
当进行拷贝构造时,被拷贝的数据是个二维数组时,就会出现两次析构的问题
vector<vector<int>> vv1{{1,2},{3,4}};//vv1是一个两行两列的二维数组
vector<vector<int>> vv2(vv1);
具体原因看下图:
memcpy只是按照字节进行
浅拷贝,vv1的_start只是拷贝给了vv2的_start,但是没有将其所指向的这个二维数组拷贝给vv2
所以对vv1和vv2进行析构时,会对同一块二维数组这个空间进行两次释放。
2.reserve的深度解析
从上面我们得知了memcpy造成的深浅拷贝问题后,我们来剖析一下reserve,其实reserve其实也是深浅拷贝的问题
如果我们要开辟的空间大于其实际长度,那么就要重新开辟空间,再将原来空间的数据存放到现在的空间中,如果我们继续使用memcpy来进行拷贝的话,那么当vector中存放的是自定义的数据时,memcpy只是浅拷贝,和上述拷贝构造问题一样会出现二次析构的问题。
以下是使用了memcpy的reserve:
void reserve(size_t n) {if (n > capacity()) {T* tmp = new T[n];size_t old = size();//保存之前的有效长度if (_start) {memcpy(tmp, _start, sizeof(T) * old);//注意:这里memcpy是浅拷贝,如果T为自定义类型,会造成两次析构}_start = tmp;_finish = _start + old;_end_of_storage = _start + n;}
}
3.解决办法
要解决深浅拷贝问题,我们可以将原来空间中的数据依次插入到新的空间中,而不是使用memcpy
解决拷贝构造浅拷贝问题:
myvector(const myvector<T>& v) {reserve(v.capacity());for (auto e : v) {push_back(e);}
}
解决reserve的浅拷贝问题:
其实也是一个个赋值给新的空间来避免浅拷贝
void reserve(size_t n) {if (n > capacity()) {T* tmp = new T[n];size_t old = size();//保存之前的有效长度if (_start) {//memcpy(tmp, _start, sizeof(T) * old);//注意:这里memcpy是浅拷贝,如果T为自定义类型,会造成两次析构for (size_t i = 0; i < old; i++) {tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old;_end_of_storage = _start + n;}
}