ITPub博客

首页 > Linux操作系统 > Linux操作系统 > c++ vector删除元素

c++ vector删除元素

原创 Linux操作系统 作者:licup123 时间:2009-01-05 14:03:56 0 删除 编辑
stl确是套很漂亮的算法和数据结构库. 但是初用stl的人往往会遇上很多问题.
从一个容器中删除元素,是很常用的操作,但是也是初学者常会犯错误的地方,删除map和list中元素可能会犯迭代器失效的错误. vector是stl里很常用的一个容器, 和map,list等容器相比, 从vector中删符合某些条件的元素有更多的麻烦.
比如,我们要完成如下的任务.
有下面的类
class AA
{
public
:
 AA():n(
0
){}
 AA(
int
 b):n(b){}
 
int
 n;
};

 有个vector
vector vaa;
一个list
list 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,为了避免拷贝,我们用指针传递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的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类型的元素.

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10697500/viewspace-526735/,如需转载,请注明出处,否则将追究法律责任。

上一篇: 归纳热备份机制
请登录后发表评论 登录
全部评论

注册时间:2008-06-22

  • 博文量
    51
  • 访问量
    119200