c++ vector刪除元素

來源:互聯網
上載者:User
   現在用stl的人越來越多, stl確實是套很漂亮的演算法和資料結構庫. 但是初用stl的人往往會遇上很多問題.
從一個容器中刪除元素,是很常用的操作,但是也是初學者常會犯錯誤的地方,刪除map和list中元素可能會犯迭代器失效的錯誤. vector是stl裡很常用的一個容器, 和map,list等容器相比, 從vector中刪符合某些條件的元素有更多的麻煩.
比如,我們要完成如下的任務.
有下面的類class AA
{
public:
 AA():n(0){}
 AA(int b):n(b){}
 int n;
};

 有個vector
vector<AA> vaa;
一個list
list<int> intList;

    現在需要執行這樣的操作, 刪除vaa裡所有成員變數n在intList裡的所有元素.那麼, 應該怎麼做呢?我們可以有下列選擇:
1 手寫迴圈
    仿照list的刪除方法.

vector<AA>::iterator ite = vaa.begin();
for (; ite != vaa.end(); )
{
    if (find(intList.begin(), intList.end(),ite->n) != intList.end())
        vaa.erase(++ite);
    else
        ++ite;
}

 

    一運行就會發現不行了, vector的erase的特點是, 被刪除的元素和之後的所有元素的iterator都失效了, 即使儲存了後面一個iterator, 也不能繼續遍曆了. 對於這種連續儲存的序列, 應該是把不需要的元素用需要的代替, 然後把結尾不要的元素刪除.像這樣:

 

    vector<AA>::iterator ite = vaa.begin();
    vector<AA>::iterator dest = ite;
    for(; ite != vaa.end(); ++ite)
    {
        if (find(intList.begin(), intList.end(),ite->n) == intList.end())
        {
            *dest++ = *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<int>,為了避免拷貝,我們用指標傳遞list

 

    bool inTheList( AA aa, const list<int> *lint)
    {
        return find(lint->begin(), lint->end(), aa.n) != lint->end();
    }

 

    要把這個兩個參數的函數綁定上一個參數變成一個參數的函數, 可以使用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)
{
    return find(lint->begin(), lint->end(), n) != lint->end();
}

 

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
{
public:
    InListFunctor(const list<int> &lint):m_list(lint)
    {}
    bool operator ()(AA a)
    {
        return find(m_list.begin(), m_list.end(), a.n) != m_list.end();
    }
private:
    const list<int> &m_list;
} ;

 

 

這樣就可以直接傳給remove_if了, InListFunctor的建構函式接受一個list<int>的const引用, 可以把要比較的list傳進去.

 

vaa.erase(remove_if(vaa.begin(), vaa.end(), InListFunctor(intList)), vaa.end());

 

通過自己定義的函數子,可以構造很複雜的比較條件,更加方便和自由.

 

4, 用boost::lambda, 構造匿名函數.

上面兩個方法都有個共 同的缺點, 要麼要定義一個函數, 要麼要定義一個類型, 這都會給一個類裡添加不必要的東西,這在實際編程中會讓人覺得不爽. 用boost::lambda可以構造匿名函數子, 不會給類的名字空間帶來汙染. 不過這個工作對boost::lambda來說有點複雜,需要包含下面三個boost::lambda標頭檔,開啟boost::lambda的名字空 間.

 

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/algorithm.hpp>
using namespace boost::lambda;

 

這個刪除操作可以寫成:

 

    vaa.erase(remove_if(vaa.begin(), 
        vaa.end(), 
        bind(ll::find(), 
            intList.begin(), 
            intList.end(),(&_1)->*&AA::n)!=intList.end()),
        vaa.end());

 

看起來有點複雜,關於boost::lambda的具體用法, 可以參考它的文檔. 我一句兩句也說不清. 上式中_1是lambda的關鍵, 指的是產生的函數的第一個參數. 這裡也就是AA類型的元素.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.