STL 迴圈刪除容器中元素的方法和陷阱

2021-09-30 09:30:15 字數 2867 閱讀 9075

演算法大師donald knuth:不成熟的優化是一切惡果的根源(permature optimization is the root of all evil )。

stl中的容器主要是兩種:序列式容器和關聯式容器。

下面講到的都是我在開發中曾經遇到過的一些問題,有些例子我做了修改,我想初學stl的人基本都會遇到這些問題。

序列式容器:vector list string等等

關聯式容器:set map等

1、容器的選擇,到底要選什麼容器應該根據你的需求和以後的擴充套件來選,可以參考effective stl上的某個條款,說的很清楚,我就不扯了。

2、怎樣正確的刪除序列式容器中的資料(迴圈刪除),這裡以vevtor為例,至少有兩大種方法,stl中任何容器都可以使用迭代器進行元素的遍歷,當需要在遍歷中刪除某些元素時,容器中元素的布局(位置或者排序)會隨之改變,當前迭代器所指示的元素也會發生變化,這時繼續遞增或者遞減迭代器進行後續元素的遍歷時就要特別小心。容器的元素刪除方法很大程度上依賴於作業系統上stl的實現版本,不同的系統和stl版本均有不同的定義,當然我們這裡以stl的標準來說明。

自己迭代實現刪除:

vector::iterator itvec = vectint.begin();

// 刪除所有值為1022的元素

for ( ; itvec != vectint.end(); )

上面是正確的實現,容易犯的錯誤是在erase後還繼續++invect,這是錯誤的,vector內部實現erase之後itvect實際上已經指向了下乙個元素,你再自增就會跳過乙個元素,會引發什麼問題就很難說了,如果剛好跳過了最後乙個元素,還有可能程式在這個地方崩潰。list也可以用這種方法,同時list還可以用下面的方法,只有一點小改動:

list::iterator itlist = listint.begin();

// 刪除所有值為1022的元素

for ( ; itlist != listint.end(); )

vector不能用上面那種方法是因為vector內部是一塊連續的記憶體,而list則是鍊錶。

使用stl演算法庫中的remove remove_if來進行刪除:

呼叫stl中演算法 remove或者remove_if的陷阱,並沒有真正的刪除(你可以此時呼叫size方法來驗證這一點),然後應該使用erase刪除殘餘資料,remove或者remove_if的實現原理是用後面 未被刪除的元素 來覆蓋前面 應該被刪除的元素。返回值forwarditerator指向經移除後的最後元素的下一位置。如vector,執行remove(),希望移除所有值為3的元素,結果為,返回值forwarditerator指向第5個元素。即:

0 1 2 3 3 4 移除前

0 1 2 4 3 4 移除後

這個方法對於自定義型別要自己實現訪函式(函式物件),這裡就不舉例了。

兩類方法的對比,其實後面的方法效率要好,如果資料大,用後面的方法,因為針對vector的話,用後面的方法不會像自己用迴圈去刪除的時候,頻繁的拷貝移動記憶體。

3、關聯式容器迴圈刪除

for(iter = map.begin(); iter != map.end();)

因為關聯式容器的erase並不返回下乙個元素的迭代器,因此不能用iter = map.erase(iter)這樣的表示式(有些版本的stl實現又可以這樣寫,我建議還是使用 map.erase(iter++)保證移植性)。

關聯容器同樣也可以用remove等來刪除元素。

4、謹慎對待操作stl容器的方法begin end size的優化,常見是將他們提取到迴圈的外部,如果你執行刪除操作,可能導致錯誤,因為在進行刪除操作的時候,容器的end,size很有可能隨時變化。list容器的end一直不會變化,但是放在for裡面,編譯器也會優化。除非你能保證for迴圈中不會改變容器的大小。

附錄:map使用:

第一種:用insert函式插入pair資料

mapmapstudent;

mapstudent.insert(pair(1, 「student_one」));

my_map.insert(make_pair("d",4));

第二種:用insert函式插入value_type資料

mapmapstudent;

mapstudent.insert(map::value_type (1, 「student_one」));

第三種:用陣列方式插入資料           

mapmapstudent;

mapstudent[1] =  「student_one」;

mapstudent[2] = 「student_two」

1.連續記憶體序列容器(vector,string,deque)

序列容器的erase方法返回值是指向緊接在被刪除元素之後的元素的有效迭代器,可以根據這個返回值來安全刪除元素。

vectorc;

for(vector::iterator it = c.begin(); it != c.end();)

2.關聯容器(set,multiset,map,multimap)

關聯容器的erase方法沒有返回值,被刪除的迭代器失效,所以刪除前必須確保能得到下乙個迭代器,可以用「後置遞增迭代器」技術。

mapm;

for(map::iterator it = m.begin(); it != m.end();)

m.erase得到it的乙個副本,在erase真正開始之前it已經遞增了。

所以erase得到了當前的迭代器,在erase內部工作開始之前it已經++了,正好滿足我們的需要。

3.非連續記憶體序列容器(list)

只所以單獨列出來是因為以上兩種方法對list都適用,可依自己喜好選擇。

//了解iterator實現細節。。

stl容器迴圈刪除元素總結

程式中有一段 是關於stl迴圈刪除的內容,大體邏輯如下 實際邏輯要複雜的多,不適合用std remove系列函式 int main int argc,char argv auto it vec vec.begin while it vec vec.end it vec return 0 儘管平常都是...

安全刪除STL容器元素

stl容器迭代過程中刪除元素技巧 序列容器的erase方法返回值是指向緊接在被刪除元素之後的元素的有效迭代器,可以根據這個返回值來安全刪除元素。vectorc for vector iterator it c.begin it c.end 關聯容器的 erase 方法沒有返回值,被刪除的迭代器失效,...

STL容器刪除元素的陷阱

今天看scott meyers大師的stl的用法,看到了我前段時間犯的乙個錯誤,發現我寫的 和他提到錯誤 幾乎一模一樣,有關stl容器刪除元素的問題,錯誤的 如下 std vectormfriendlist std vector iterator iter mfriendlist.begin for...