乙個合格的容器(container)必須支援增刪改查,c++的順序容器和關聯容器也不例外。
不同於insert和search,erase操作涉及到多種情況,尤其是和迭代器相結合的時候。
本文總結常用c++常用容器的erase的正確做法。
vector是stl中唯一乙個保證記憶體連續的容器,也是我們最常用的容器。
刪除值為val的元素
void erasevec()
; vec.erase(std::remove(vec.begin(), vec.end(), 5), vec.end());
}
上述**是最標準的在vector中刪除值為5的元素的方法,你會發現有兩個很容易讓人產生誤會的介面:
erase和remove,我們看一下他們的原型:
iterator erase (iterator
begin, iterator
end);
//tips : 刪除容器中迭代器在[first; last)範圍內的元素
//return : 最後乙個被刪除的元素的下乙個元素的迭代器
iterator
remove(iterator
begin, iterator
end, val);
//tips : 移除容器中迭代器在[first; last)範圍內的值為val的元素
//return : 被成功移除的之後,剩餘的有效元素的下乙個元素的迭代器
我們可以看到,remove做的操作是移除而erase是刪除,這兩者到底有什麼區別呢?
我們剛建立的vec的記憶體布局如下:
在remove(begin,end,5)之後,記憶體布局如下:
它僅僅做了移除操作,把不要的元素放在了末尾(這是其中一種實現,你不能對67後面的兩個位置的值做出任何假設),但是capacity甚至size都沒有改變,也就是說元素並沒有被刪除。
更可怕的是,如果你做了下列操作:
std::remove(vec.begin(), vec.end(), 5);
for (auto it = vec.begin(); it != vec.end(); ++it)
你會發現輸出的值是12346755(或者可能是132456767),元素根本沒有被刪除
在vec.erase(std::find(vec.begin(), vec.end(), 5))
操作之後,記憶體布局:
元素被真正的刪除了。
再次強調一下最正確的刪除方式:
void erasevec()
; vec.erase(std::remove(vec.begin(), vec.end(), 5), vec.end());
}
刪除滿足某個條件的元素void erasevecif()
; vec.erase(std::remove_if(vec.begin(), vec.end(),
(int elem)
), vec.end());
}
迴圈刪除
我們再看一種比較容易犯錯的情況,那就是在迴圈種刪除某個迭代器:
void eraseveclooperror()
; for (std::vector
::iterator it = vec.begin(); it != vec.end(); ++it)}}
這裡是錯誤的示範,為何呢?
原因在於,vec.erase(it)
會讓vec中的迭代器失效,直接it++會讓程式崩潰,所以這裡正確的做法應該是合理利用erase的返回值:
void erasevecloopcorrect()
; for (std::vector
::iterator it = vec.begin(); it != vec.end(); /*do nothing*/)
else}}
deque和vec的方法完全相同,不再贅述
string和vec的方法完全相同,不再贅述
list和vector、dequeue、string不同,它的刪除更為簡單:
void eraselist()
; ls.remove(5);
ls.remove_if((int elem) );
}
對於list,直接使用remove方法即可。(對於list而言移除 == 刪除)
當然,你仍可以使用erase介面,只不過remove方法更為效率一點。
迴圈刪除的方式
void eraselistloop()
; for (std::list
::iterator it = ls.begin(); it != ls.end(); )
else}}
map和unordered_map
由於map和unordered_map的特殊性(他們的元素都是乙個pair),c++沒有提供這樣的介面
int x = 5;
container.erase(x)
container.remove(x)
這樣的介面,如果想完成類似的需求那麼只能:
void eraseset()
}
這個是比較符合我們的直覺的,但是如果在迴圈種刪除滿足某種條件的迭代器,那麼就需要好好注意了:
void erasemaperror()}}
處於同樣(見上文,vector:迴圈刪除)的原因,我們不能再迴圈種直接it++,可是對於關聯容器,erase這個方法沒有返回iterator,所以正確的做法是:
void erasemapcorrect()
else}}
set和unordered_set
set和unordered_set可以直接使用erase方法,但是remove不起作用,使用remove會編譯錯誤:
void eraseset()
迴圈刪除則同map和unordered_map,不再贅述。 容器 順序容器 關聯容器
容器分為 順序容器 關聯容器,示意圖如下 順序容器 只儲存值,關聯容器 key value形式,關聯容器的乙個元素包含兩個部分 鍵值對 key value 有序容器 底層實現為紅黑樹,即容器內的key是有序的 無序容器 底層實現為hash table,雜湊表 區別 順序容器通過元素在容器中的位置順序...
關聯容器和順序容器的差別
關聯容器和順序容器的本質差別在於關聯容器通過鍵 key 儲存和讀取元素,順序容器則通過元素在容器中的位置順序儲存和訪問元素。順序容器list vector deque和關聯容器map set型別分別適用的情況 1 list 型別適用於需要在容器中間插入和刪除元素的情況,例如以無序方式讀入一系列學生資...
順序容器與關聯容器的不同
想要理解關聯容器與順序容器的不同,最關鍵的是理解其基礎的資料結構,這樣就很自然的理解它所表現出來的性質。兩類容器的根本差別在於,順序容器中的元素是 順序 儲存的 鍊錶容器中的元素雖然不是在記憶體中 連續 儲存的,但仍然是按 順序 儲存的 理解順序的關鍵,是理解容器支援的操作形式以及效率。對vecto...