現在用stl的人越來越多, stl確實是套很漂亮的演算法和資料結構庫. 但是初用stl的人往往會遇上很多問題.
從乙個容器中刪除元素,是很常用的操作,但是也是初學者常會犯錯誤的地方,刪除map和list中元素可能會犯迭代器失效的錯誤. vector是stl裡很常用的乙個容器, 和map,list等容器相比, 從vector中刪符合某些條件的元素有更多的麻煩.
比如,我們要完成如下的任務.
有下面的類
class aa
aa( int b):n(b){}
int n;};
有個vector
vectorvaa;
乙個list
listintlist;
現在需要執行這樣的操作, 刪除vaa裡所有成員變數n在intlist裡的所有元素.那麼, 應該怎麼做呢?我們可以有下列選擇:
1 手寫迴圈
仿照list的刪除方法.
vector < aa > ::iterator ite = vaa.begin();
for (; ite != vaa.end(); )
一執行就會發現不行了, vector的erase的特點是, 被刪除的元素和之後的所有元素的iterator都失效了, 即使儲存了後面乙個iterator, 也不能繼續遍歷了. 對於這種連續儲存的序列, 應該是把不需要的元素用需要的代替, 然後把結尾不要的元素刪除.像這樣:
vector < aa > ::iterator ite = vaa.begin();
vector < aa > ::iterator dest = ite;
for (; ite != vaa.end(); ++ ite)
}vaa.erase(dest, vaa.end());
2. 使用remove_if, 寫乙個判斷函式作為條件.
像上面那樣寫迴圈,麻煩,容易錯而且不好讀, stl提供了乙個演算法remove_if可以不用自己寫迴圈,完成上面那個迴圈的功能, 就是把不要的
元素用需要的元素代替, 返回結尾處的iterator.remove_if的原型為
template < class forwarditerator, class predicate >
forwarditerator remove_if(forwarditerator first, forwarditerator last,predicate pred);
pred是乙個函式子,用來作為判斷條件. 函式子是可以按照函式呼叫的語法來使用的型別, 它可以是乙個函式指標, 也可以是乙個過載了operator()的型別.這裡pred要求是返回值是bool,有乙個引數的函式子, 引數型別就是容器裡元素的型別, 對每個元素執行這個函式, 返回true就會被remove.
所以,我們需要先寫乙個函式來判斷乙個aa型別的變數是否滿足條件. 但是, 這個函式顯然需要兩個引數, 乙個aa 和乙個list,為了避免拷貝,我們用指標傳遞list
bool inthelist( aa aa, const list < int > * lint)
要把這個兩個引數的函式繫結上乙個引數變成乙個引數的函式, 可以使用stl裡的bind2nd函式,原型如下
template < class adaptablebinaryfunction, class t >
binder2nd < adaptablebinaryfunction >
bind2nd( const adaptablebinaryfunction & f, const t & c);
這個函式並不會被執行, 編譯器只是靠它來做型別推導, 它會返回乙個adaptable unary function 型別. 它的第乙個引數是乙個adaptable binary function, 它是乙個重定義了operator()的型別,不能直接傳乙個函式指標, 所以我們需要ptr_fun函式,ptr_fun對雙參函式指標的過載的原型為:
template < class arg1, class arg2, class result >
pointer_to_binary_function < arg1, arg2, result >
ptr_fun(result ( * x)(arg1, arg2));
這個函式也是用來做型別推導的, 可以返回乙個adaptable unary function.
綜合以上各個函式, 於是就可以這樣寫了:
vaa.erase(remove_if(vaa.begin(), vaa.end(),bind2nd(ptr_fun(inthelist), & intlist)), vaa.end());
注意, 可能是vc6的bug, 如果inlist是乙個類的靜態成員函式, 上面的寫法在vc6裡無法編譯, vc6不能推導出函式子的型別,上面的寫法在vc8和gcc中是可以的.對於vc6,需要顯式的告訴編譯器我們傳的是函式指標,像下面這樣
vaa.erase(remove_if(vaa.begin(), vaa.end(),bind2nd(ptr_fun( & inthelist), & intlist)), vaa.end());
我們也可以讓inthelist是aa的乙個成員函式
bool aa::inthelist( const list < int > * lint)
stl提供了一套把成員函式轉為單參或雙參函式子的函式,mem_fun1_ref,這裡我們用上面的刪除操作就可以寫成:
vaa.erase(remove_if(vaa.begin(), vaa.end(),bind2nd(mem_fun1_ref( & aa::inthelist), & intlist)), vaa.end());
3, 還是用remove_if, 自己定義判斷條件的函式子型別
上面那套轉換和繫結肯定能讓人抓狂, 使用函式指標來傳遞判斷條件也比較低效. 我們可以自己定義乙個型別
class inlistfunctor
bool operator ()(aa a)
private :
const list < int > & m_list;
} ;
這樣就可以直接傳給remove_if了, inlistfunctor的建構函式接受乙個list的const引用, 可以把要比較的list傳進去.
vaa.erase(remove_if(vaa.begin(), vaa.end(), inlistfunctor(intlist)), vaa.end());
通過自己定義的函式子,可以構造很複雜的比較條件,更加方便和自由.
4, 用boost::lambda, 構造匿名函式.
上面兩個方法都有個共同的缺點, 要麼要定義乙個函式, 要麼要定義乙個型別, 這都會給乙個類裡新增不必要的東西,這在實際程式設計中會讓人覺得不爽. c++0x 裡面提供了 lambda ,可以構造匿名函式。這個特性已經在 vs2010 裡面實現了。於是可以用下面的**來實現刪除。
vaa.erase(remove_if(vaa.begin(), vaa.end(),
[&intlist](const aa &a)),
vaa.end());
刪除所有偶數項,並列印出刪除的項
1. vector/queue
正確方法1:
void
erase(vector
<
int>&v)
else
++vi;}}
正確方法2:
void
erase2(vector
<
int>&v)
else
++ri;}}
2.map/list
正確方法
void
erase(map
<
int,
int>&m)
else
++mi;}}
怎麼從vector裡刪除元素
現在用stl的人越來越多,stl確實是套很漂亮的演算法和資料結構庫.但是初用stl的人往往會遇上很多問題.從乙個容器中刪除元素,是很常用的操作,但是也是初學者常會犯錯誤的地方,刪除map和list中元素可能會犯迭代器失效的錯誤.vector是stl裡很常用的乙個容器,和map,list等容器相比,從...
Vector容器刪除元素
使用vector容器也有一段時間了,但是對於他的刪除操作還是有點疑問,今天就總結一下。vector資料儲存是一段預先分配好大小的記憶體連續的空間,插入資料和刪除資料都會引起後面資料記憶體的整體移動。今天就說說刪除操作吧 1 刪除最後的元素 直接使用pop back 就可以了,這個沒什麼好說的 2 刪...
vector 之刪除元素
刪除指定位置的元素 刪除vector中第5個位置的元素 1 vector vec 執行vector初始化操作 2 vector iterator iter vec.begin 5 獲取第五個元素的iterator 3 vec.erase iter 刪除第五個元素 刪除重複元素 刪除vector中的重...