C++從序列容器中刪除元素的四種方法
一、簡介
本文討論從集合中刪除元素的STL算法。從C++集合中刪除一個元素可能不復雜,也可能有點復雜。
刪除元素的方法在序列容器和關聯(lián)容器之間是非常不同的。在序列容器中,vector 和 string 是最常用的。但這里也會介紹 deque 和 list 以供全面了解,盡管在一般情況下可能不會使用它們。
至少有四種方法可以指定從任何容器中刪除哪些值:
- 在給定位置(或在兩個給定位置之間)刪除元素;
- 刪除等于某個值的元素;
- 刪除滿足某個謂詞的元素
- 以及刪除重復項。
下面來看看如何在STL序列容器中實現(xiàn)這四種命令。
二、移除給定位置的元素
這是最簡單的方法。如果是一個序列容器,可以通過調(diào)用erase。比如:
c.erase(position);
要移除由迭代器first和last組成的子范圍中的元素,可以這么調(diào)用:
c.erase(first, last);
與STL中迭代器表示的所有范圍一樣,子范圍包括first,而不包括last。last指向“past-the-end”元素,類似于容器的結束迭代器。
注意,對于vector和string,所有指向被移除對象所在位置和之后元素的迭代器都無效。因為所有這些元素都被erase函數(shù)調(diào)用移除了。
對于deque來說,會有一點點不同:參考cppreference.com,所有迭代器和引用都無效,除非被刪除的元素位于容器的末尾或開頭,在這種情況下,只有迭代器和對被刪除元素的引用無效。
- 如果刪除的元素位于
deque的中間位置,則所有指向該元素以及之后位置的迭代器和引用都會失效。 - 如果刪除的是末尾元素,那么僅僅指向這個末尾元素的迭代器和引用會失效,其余保持有效。
- 如果刪除的是開頭元素,同樣只有指向這個開頭元素的迭代器和引用會失效。
這erase很簡單,只是熱身。下面還有復雜的,接著閱讀學習吧。
三、移除與某個值相等的元素
3.1、序列容器vector、deque、string
這些容器沒有刪除值的方法,因此需要使用std::remove算法。該算法取一個要刪除的范圍和一個值,并上移所有要保留的元素。
例如,在這個整數(shù)范圍內(nèi)調(diào)用std::remove并帶值42,會有以下行為:

注意,在范圍末尾剩下的元素的值是未指定的。盡管有些實現(xiàn)可以將最初位于集合末尾的元素保留下來,但這是不可靠的。
要記住,在STL的設計中,算法只與迭代器交互,而不直接與容器交互,因此容器并不知道算法的效果。例如,它的size并沒有縮小。
為了有效地從集合中刪除元素,需要使用在本文前面講到的erase方法。為此,要注意到std::remove返回一個迭代器,該迭代器指向不應被刪除的元素范圍內(nèi)的“past-the-end”元素。即,要刪除的元素位于std::remove返回的迭代器定義的范圍和集合的末尾。
因此,要有效地從vector、deque或string對象中刪除值,可以這樣寫:
v.erase(std::remove(begin(v), end(v), 42), end(v));
3.2、封裝成模板方法
這是C++的習慣用法,如果在代碼中遇到它,必須知道。但是,坦白地講,不覺得用這么多代碼來表達這么簡單的事情有點多嗎?難道不喜歡像下面這樣寫嗎?
v.remove(42); // or v.erase(42);
也可以給它添加一些重載來操作deque和string對象:
template<typename T>
void erase(std::deque<T>& deque, T const& value)
{
deque.erase(std::remove(begin(deque), end(deque), value), end(deque));
}
void erase(std::string& string, char letter)
{
string.erase(std::remove(begin(string), end(string), letter), end(string));
}
非常建議實現(xiàn)這些輔助函數(shù),特別是對于最常用的vector。這可以避免標準習慣用法所帶來的迭代器的糾纏。
甚至在C++標準中,就有學者提出了一個增加這種泛型函數(shù)的建議。很遺憾的是,它還沒有在C++ 17中實現(xiàn)。
3.3、list的remove成員函數(shù)
為了全面起見,這里提一下要從list中刪除一個元素,有一個叫做remove的方法,例如:
l.remove(42);
由于它不提供隨機訪問迭代器,在列表上使用std::remove算法會使列表變得比現(xiàn)在更慢。
四、刪除滿足謂詞的元素
前面已經(jīng)看到了如何從序列容器中刪除所有等于某個值的元素,比如42。那么,如何移除滿足謂詞func的元素?其實,這完全一樣,只是需要使用 remove_if 而不是 remove。
所以只需要替換:
remove為remove_if;- 42為
func。
std::remove_if(begin(string), end(string), func)
和上一節(jié)一樣。依然建議編寫一個名為erase_if的自由函數(shù),以避免大量迭代器的出現(xiàn);并且list同樣有一個名為remove_if的成員方法。因此,為了遵循“不要重復”的原則和避免文章篇幅過長,這里不再對remove_if進行更多討論。
五、從序列容器中刪除重復項
從序列容器中刪除重復項的STL算法是std::unique。但是要注意!unique只刪除相鄰的重復項,而不刪除整個集合中的重復項。它具有線性復雜度。
除此之外,unique和remove非常相似。它只壓縮集合的元素,而不能改變?nèi)萜鞅旧韘ize。因此,需要在容器上調(diào)用erase才能有效地刪除重復項:
vector.erase(std::unique(begin(v), end(v)), end(v));
和remove一樣,封裝一個方便的函數(shù)是必要的:
template<typename T>
void unique(std::vector<T>& vector)
{
vector.erase(std::unique(begin(vector), end(vector)), end(vector));
}
template<typename T>
void unique(std::deque<T>& deque)
{
deque.erase(std::unique(begin(deque), end(deque)), end(deque));
}
void unique(std::string& string)
{
string.erase(std::unique(begin(string), end(string)), end(string));
}
與remove類似,std::list有一個unique的成員方法。
六、總結
這就是C++中從序列容器中刪除元素的方法。
在C++中,從序列容器中刪除元素的方法多種多樣,每種方法都有其適用的場景和使用方式。
移除給定位置的元素:可以使用
erase方法,通過指定要刪除的元素位置或者給定范圍的迭代器來實現(xiàn)。移除與某個值相等的元素:對于
vector、deque和string等序列容器,可以使用std::remove算法,并結合erase方法來刪除指定值的元素;或者封裝成模板函數(shù)來簡化操作。刪除滿足謂詞的元素:使用
std::remove_if算法結合erase方法,可以刪除滿足指定謂詞條件的元素。從序列容器中刪除重復項:利用
std::unique算法可以刪除相鄰的重復項,但需要注意該算法只刪除相鄰的重復項,并且不能改變?nèi)萜鞯拇笮?,需要配?code>erase方法來實現(xiàn)刪除整個容器中的重復項。
以上就是C++從序列容器中刪除元素的四種方法的詳細內(nèi)容,更多關于C++序列容器刪除元素的資料請關注腳本之家其它相關文章!

