Chinaunix首页 | 论坛 | 博客
  • 博客访问: 830783
  • 博文数量: 168
  • 博客积分: 5431
  • 博客等级: 大校
  • 技术积分: 1560
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-22 11:56
文章存档

2015年(2)

2014年(1)

2013年(12)

2012年(12)

2011年(15)

2010年(5)

2009年(16)

2008年(41)

2007年(64)

分类: C/C++

2007-12-19 13:47:10

erase方法的问题以及remove_if的使用注意事项


    在使用vector的时候,遇到一个问题,找了一些资料总算弄清楚了原因并学习到了新的知识。为了避免忘记,记录如下:
   
    例子:
    int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    vector vecInt(a, a+10);
    vector::iterator it;
    for(it=vecInt.begin(); it != vecInt.end(); it++){
       if(*it % 3 == 0)
          vecInt.erase(it);
    }

    上面的代码看起来没有问题;实际的结果却令人迷惑(不同长度的a[]会影响结果)。
       第一种,返回正确!(不是有问题的吗?知识巧合);
       第二种,返回一个莫名其妙的序列;
       第三种,segment fault。
    上面的三种结果可以自己动手尝试一下。下面讲解原因。

    罪魁祸首是erase方法以及iterator这两个在c++中使用广泛的操作符!iterator是一个迭代子,当与erase方法仪一起使用的条件下会产生严重的问题(结果可能是上面的一种)。erase将iterator对应的元素remove掉以后会返回一个iterator,问题就出在这个返回的iterator指向哪里,实际上它指向已经removed的元素的下一个元素;这个时候,如果继续作扫面的操作则会有两种情况存在:
    1.remove的是最后一个元素,这样就会导致返回的iterator指向一个不存在的地方
    2.并没有指向最后的元素,但是!,由于上次的erase的时候iterator已经指向了“下”一个元素了,这个时候再次对iterator进行++(循环中操作)则会错过一个元素没有检验是否符合条件,从而产生问题。

    问题已经明确了解决的方法比较简单(很讨厌):
    我们使用c++ STL中的algorithm里面的remove_if方法来完成erase的操作。


    vecInt.erase(remove_if(vecInt.begin(), vecInt.end(), bind2nd(modulus(), 2)));

    这样,就可以按照条件将vector中的不符合/符合条件的元素remove掉了。
    补充一点:modulus()可以自定义,例如:
       template class myOperator: public unary_function{
             public:
                bool operator ()(T& t){
                     return t % 2 == 0;
                   
                }
        };
    vecInt.erase(remove_if(vecInt.begin(), vecInt.end(), myOperator()));

    再者,不知道什么原因使用如下形式删除多余元素的方法不正常:
        it0 = remove_if(vet0.begin(), vet0.end(), bind2nd(modulus(), 2));
    for(; it0 != vet0.end(); it0++){
            printf("The odd is %d\n", *it0);
            vet0.erase(it0);
    }


关于C++中inline函数的总结
   

    C中只要在function前面添加inline关键字即可告诉编译器不要为该函数分配堆栈等资源而是直接将代码“插入”自己的“父”function;那么C++中又是如何?

    对于直接编写在class体内的function是默认inline的,这一点没有什么可说的。

    对于编写在class外部的function如果是没有添加关键字“inline”,那么一定不是内联的。

    如何既编写在class外部又是内联的呢?这个也是不难的:
    1)在class体内的声明以及外部定义都添加关键字“inline”,并且有一点需要注意,class声明与定义function的位置应该处于一个文件中,准确的说是“同一个”编译单元内部,否则也是没有达到内联的目的。


C++ function模板的使用(高级特性)
   

    模板是C++中很强大的特性可以使得我们的程序具有更好的通用与扩展性。而我们也知道,世间没有免费的午餐,我们需要花精力来学习这个特性。说心里话,这可不是一件简单的事情。下面我们看看它的用法。对于比较难的或者入门以及不常用的特性这里不做详细介绍。

    模板显示实例化声明

    显示实例化可以控制模板实例化的时间点(这点有点类似design pattern里面控制class构造以及析构时间)。在编译器比较老的时代,由于编译器支持的特性比较少,为避免实例化的时间点不同而产生的问题,提出了这样一个特性。现在看看这个特性的用法:
    template T function0(T t, int y){ ... }
针对以上的定义可以通过如下声明控制模板实例化的时间。注意:对于给定实例参数,在一个application中只能声明一次!
    template int* function0(int*, int y){ ... }//只能一次;
    template float function1(float, int y){ ... }这种也只能一次!

    模板显式特化定义

    看下面的实例引出模板显式化定于的必要性。
    template T function1(T t, float f){ cout<    如果我们使用int,float,char以及一个自定义的class来初始化这模板函数可以正常给出输出结果;但是,如果我们希望传入char*,int* const float*等类型的时候呢?
实际上这个时候我们的函数不=模板已经不能符合要求了。怎么办?重新定义一个模板?太累了。重载这个模板函数(后面讲解)?有必要吗?答案都是否定的。
我们采用“模板显式特化定义”就可以解决这个问题!现在看看解决的方法:
    typedef const char* pChar;
    template<> pChar function1(pChar t, float f){ cout<<*t<<"and"<    注意:
    1)即便是原来的模板函数仅仅声明了而没有实现也可以对这种函数模板做“模板显式特化定义”。
    2)对于在多个文件中多次使用“模板显式特化定义”所定义的实例,必须多次声明。

    重载模板函数
    模板函数跟一般的函数在重载这个特性上没有什么独特之处,只是使用(是使用过程)过程中不要出现ambiguous的地方。
    示例:
    定义:
    template T tfhn(T t, int in){ cout<<"()()()()()()()()()()"<    template T tfhn(T& t, T0&t0, int in){ cout<<"Overloade T,T0 template function tfhn!"<    template T tfhn(T*t, T0&t0, T1 t1){ cout<<"Overloade T,T0,T1 template function tfhn!"<    使用:
    float fl = 1.0;
    char c = 9;
    const float* p_float = &fl;
    tfhn(p_float, 100);
    string str8("yufeng!!");
    string* p_str8 = &str8;
    tfhn(p_str8, 200);
    tfhn(fl, c, 100);
    tfhn(&c, fl, 100);//如果tfhn(p_str8, fl, 100);会出现歧意!!!
    3)另外,普通函数的名字可以与模板的名字相同!!
    4)由于实例化后的函数的参数允许默认的转换,因此,存在候选函数之间的选择问题,这个会根据编译器的实现来完成,
不过程序员应该明确知道自己定义的模板函数重载在项目中等级以便正确实现自己的功能。


STL中数组容器的使用技巧


    我们经常使用STL中vector来帮助我们解决问题。这是一个很方便的工具,但是如果不合理的使用它将会对我们的程序的性能产生极大的影响!下面就来说说,当我们申请到一块过大的内存给vector后该如何将多余的部分一次性释放(我们不是一块一块的释放,这种做法实在愚笨!)。列举一个实例:
    vector int_ve;
    int_ve.push_back(0);
    int_ve.push_back(1);
    int_ve.push_back(2);
    int_ve.push_back(3);
    其他的程序代码。
    int_ve.erase(remove(3));
    int_ve.erase(remove(2));

    经过使用后我们假设含有“2”, “3”两个元素的内存我们已经不使用了(尽管我们已经显式的做了erase的操作,但是vector的内存管理程序并没有释放这里的内存,我这里的实例只是两个元素实际上,在应用过程中可能是几千个,这时候对内存的浪费就不能忽略了),我们有没有办法主动“告诉”vector的内存管理系统释放我们并不使用的内存资源呢?答案淡然是可以,这里我介绍一种学习来的方法,实践证明是很有效的。
    方如下:
    vector (int_ve).swap(int_ve);
    解释一下这样做后发生的一系列行为:
    首先,根据int_ve构造一个临时的vector对象,这个临时对象与原来的int_tv唯一的区别是:原来内部存在一些没有释放的内存而我们在构造临时对象时,系统认为没有必要在临时对象中保留那些没有用的内存,从而使得临时对象比较“紧凑”。但是原来的对象还是没有改变啊?这个问题的答案是由swap完成的,swap后原来的int_ve的内容(全部)进入临时的对象而int_ve的内容变为临时对象的内容,这样就完成了缩减无用内存的目的。


binary_function以及unary_function的使用场合


    我们在使用STL中的vector,list,set,map,multimap的时候往往直接编写如下形式的代码而忽略了"<>"里面的后两个有默认参数的参数的存在!
    set int_set;
    set模板的原型是这样:
    template            class _Compare = std::less<_Key>,
            class _Alloc = std::allocator<_Key> >     class set;
    "_Compare", "_Alloc"是缺省的实参,由STL实现了一些默认的排序(_Compare)以及内存分配管理器(_Alloc),使用的时候如果没有传递给实例这两个参数则使用默认的策略。大多数的情况下,我们不必要关心它们,但是这里要讲解的binary_function,unary_function就与“_Compare”这个缺省参数有关!

    我们看到set缺省的_Compare参数是less模板的一个实例,自然想到我们写一个自己的"compare"满足特殊环境的需求。下面就来写一个:
   class my_less{
        public:
          bool operator()(int t, int t0){
             return t < t0;
        }
    };
    然后如下实例化set
    set int_set;
    当有元素插入int_set的时候,就会使用我们定义的function object中排序方法进行排序,但是有一个问题需要考虑的是:现在是int,那么换了long呢?char?我们一个一个来写?很好的解决方法是使用模板,重新实现如下:
    template class my_less{
       public:
          bool operator()(T& t, T& t0){
             return t < t0;
        }
    };
      模板解决了
阅读(1464) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~