??博主CSDN主頁(yè):杭電碼農(nóng)-NEO??
?
?專欄分類:C++從入門到精通?
?
??代碼倉(cāng)庫(kù):NEO的學(xué)習(xí)日記??
?
??關(guān)注我??帶你學(xué)習(xí)C++
? ????
1. 前言
在閱讀本篇文章前,一定要先看前集:
vector深度剖析(上)
本章重點(diǎn):
本章會(huì)重點(diǎn)講解vector迭代器失效問(wèn)題
以及vector中的深淺拷貝問(wèn)題
并且簡(jiǎn)單完善一下vector的自我實(shí)現(xiàn)
在此之前,我將在文章末尾把vector
自我實(shí)現(xiàn)的完整代碼分享給大家
2. 什么是迭代器失效?
首先我們要清楚一點(diǎn):
vector的每一次擴(kuò)容都不是在
原地?cái)U(kuò)容,而是新開(kāi)辟一塊兒空間后
將原先的數(shù)據(jù)拷貝到新空間
請(qǐng)看下面的代碼:
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
auto pos = find(v.begin(),v.end(),3);
v.insert(pos,30);
v.insert(pos,40);
這段代碼在3前面插入一個(gè)30和40但是這段代碼會(huì)出錯(cuò)!
為什么呢?請(qǐng)看下圖:
注:從四個(gè)數(shù)據(jù)插入為五個(gè)會(huì)擴(kuò)容
擴(kuò)容前
迭代器pos在start和finish之間擴(kuò)容后
start和finish的地址改變,pos失效
pos不再指向現(xiàn)在的位置3
迭代器失效的本質(zhì)原因是:
擴(kuò)容后start和finish的地址發(fā)生變化
指向原先位置的迭代器統(tǒng)統(tǒng)失效!
若沒(méi)發(fā)生擴(kuò)容,則一切安好!
3. 迭代器失效的經(jīng)典案例
除了前面講到的insert導(dǎo)致迭代器失效外erase函數(shù)
也會(huì)導(dǎo)致迭代器失效
請(qǐng)看下面的案例:
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(4);
v.push_back(6);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.erase(it);
}
++it;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
這段代碼在刪除順序表中所有的偶數(shù)
但是你會(huì)發(fā)現(xiàn)它并沒(méi)有刪除完
這是為啥呢?請(qǐng)看下圖的分析
erase刪除后,后面的數(shù)據(jù)會(huì)覆蓋過(guò)來(lái)
此時(shí)不讓迭代器++它也指向下一個(gè)位置
注:在VS編譯器中.只要使用了erase函數(shù)
編譯器自動(dòng)認(rèn)為此位置迭代器失效
所以在VS上進(jìn)行多次erase操作時(shí)
一定要不斷更新迭代器的位置!
4. 迭代器失效的解決方案
對(duì)于insert來(lái)說(shuō)
在pos位置使用一次insert后
不要再次直接訪問(wèn)pos迭代器
一定要更新了pos之后再去訪問(wèn)!
庫(kù)中的vector提供了返回值來(lái)解決此問(wèn)題:
insert會(huì)返回一個(gè)迭代器,此迭代器的
返回的是新插入元素的迭代器
請(qǐng)看下圖理解:
所以以后我們可以這樣寫代碼:
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);
v.push_back(7);
vector<int>::iterator it = v.begin();
while(it!=v.end())
{
it = insert(it,100);
it+=2;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
在每一個(gè)元素前插入一個(gè)100
對(duì)于erase來(lái)說(shuō)
刪除后不用再++迭代器
只用在沒(méi)刪除的時(shí)候再++
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(4);
v.push_back(5);
v.push_back(6);
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.erase(it);
}
else
{
++it;
}
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
5. 對(duì)于reserve的深度剖析
眾所周知,reserve只改變capacity大小
而不會(huì)改變size的大小
所以這樣寫代碼是有問(wèn)題的:
vector<int> vv;
vv.reserve(10);//開(kāi)辟10份空間
for(int i=0;i<10;i++)
{
vv[i]=i;
}
因?yàn)閟ize此時(shí)是0,也就是有效長(zhǎng)度為0
雖然你開(kāi)辟了10份空間,但是運(yùn)算符
操作[ ]的內(nèi)部實(shí)現(xiàn)會(huì)檢查下標(biāo):
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
所以使用reserve后直接用
[ ]
訪問(wèn)會(huì)報(bào)錯(cuò),這也是很多人會(huì)出錯(cuò)的地方!
6. vector深淺拷貝問(wèn)題
首先來(lái)看看以下代碼:
vector<vector<int>> vv(3,vector<int>(5));
這是一個(gè)二維數(shù)組,初始化為三行五列
vector<vector<int>> vv(3,vector<int>(5));
vector<vector<int>> x(vv);
這是在拷貝構(gòu)造類對(duì)象x
自我實(shí)現(xiàn)的拷貝構(gòu)造使用的是memcpy:
Vector(const Vector<T>& v)
{
assert(v._start && v._finish && v._endofsto);
_start = new T[v.capacity()];//給size或capacity都可以
memcpy(_start, v._start, sizeof(T) * v.size());
}
然而memcpy是逐個(gè)字節(jié)拷貝
當(dāng)數(shù)組是一維時(shí),用memcpy沒(méi)有問(wèn)題
但是當(dāng)數(shù)組是二維數(shù)組時(shí),會(huì)出錯(cuò)!
我們?cè)赩S上調(diào)試窗口的監(jiān)視查看地址信息:
會(huì)發(fā)現(xiàn),雖然x的地址和vv的地址不同
但是vv中的迭代器和x中的迭代器
的地址是相同的也就是指向同一份空間
可以用下圖來(lái)理解這個(gè)過(guò)程:
7. vector深淺拷貝的解決方法
由于這種深淺拷貝問(wèn)題是因?yàn)閙emcpy
導(dǎo)致的,所以這里不能使用memcpy
只需要老實(shí)的使用一個(gè)for循環(huán)就能解決:
修改后的代碼:
Vector(const Vector<T>& v)
{
assert(v._start && v._finish && v._endofsto);
_start = new T[v.capacity()];//給size或capacity都可以
//memcpy(_start, v._start, sizeof(T) * v.size()); //使用memcpy時(shí),數(shù)組是二維數(shù)組會(huì)發(fā)生問(wèn)題
for (size_t i = 0; i < size(); i++)
{
_start[i] = v._start[i];
_finish = _start + v.size();
}
_endofsto = _start + v.capacity();
}
直接使用等號(hào)=是外部和內(nèi)部都是
原來(lái)的一份拷貝,這樣就能解決問(wèn)題了
8. 總結(jié)以及拓展
vector的自我實(shí)現(xiàn)的目的不是
為了實(shí)現(xiàn)一個(gè)比庫(kù)中更好的vector
而是為了帶大家熟悉vector的使用
并且了解了內(nèi)部實(shí)現(xiàn)后,以后用vector
時(shí)出現(xiàn)問(wèn)題可以很快的排查出來(lái)!
拓展:vector自我實(shí)現(xiàn)全部代碼鏈接:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-682334.html
gitee代碼倉(cāng)庫(kù)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-682334.html
到了這里,關(guān)于【C++進(jìn)階(三)】STL大法--vector迭代器失效&深淺拷貝問(wèn)題剖析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!