STL容器 erase的使用陷阱 (二)

来源:互联网 发布:多变量优化 编辑:程序博客网 时间:2024/05/22 04:41
接上一篇。
二、使用STL中通用算法或容器成员函数删除元素的方法
上一篇手工编写的FOR循环代码删除容器中元素的方法也有一些问题,如果判断条件特别复杂,又有循环判断的话,循环中间又有异常处理的话,++itVect的位置就要小心放置了,稍不留意就要出错,所以手工编写代码删除容器中元素的方法不太安全,代码重复,也不够优雅,要注意的地方很多。

对于这种情况,可以考虑使用STL中通用算法remove()和Remove_if()帮忙。而Remove()和Remove_if()这两个算法也有一个问题需要程序员特别小心,在通用算法中的remove(remove_if)函数并不真正从容器中删除元素,而是“应被删除元素”被其后的“未被删除的元素”覆盖。返回值ForwardIterator指向移除后的最后元素的下一个位置。
如Vector(0,1,2,3,3,4),执行Remove(),希望一处所有值为3的元素,结果为(0,1,2,4,3,4),返回值ForwardIterator指向第5个元素。
即:
0    1    2    3    3    4 移除前
0    1    2    4    3    4 移除后
移除值为3的元素,移除后3被其后的4替代,最后两位元素为残余数据。

例5:
  1. void main() {
  2.        vector<int> vectInt;
  3.        int i;
  4.        for (i = 0; i < 5; i++ ) {
  5.               vectInt.push_back( i );
  6.               if ( 3 == i ) {
  7.                      vectInt.push_back( i );
  8.               }
  9.        }
  10.        remove( vectInt.begin(), vectInt.end(), 3 );
  11.        cout << " after deleted , size = " << vectInt.size() << endl;
  12.        for ( i = 0; i < vectInt.size();; i++ ) {
  13.               cout << "i = " << i << " , " << vectInt[i] << endl;
  14.        }
  15. }
  16. 运行结果为:
  17. after deleted , size = 6 // 从这行可以看出,移除后容器的大小没变
  18. i = 0 , 0
  19. i = 1 , 1
  20. i = 2 , 2
  21. i = 3 , 4 //从这行可以看出:“应被删除的元素”3 被其后的“未被删除的元素”4覆盖
  22. i = 4 , 3
  23. i = 5 , 4     
  所以要彻底删除还应该把后面的残余数据删除掉,这可以通过调用容器的成员函数ERASE()做到。

例6:
  1. void main() {
  2.        vector<int> vectInt;
  3.        int i;
  4.        for (i = 0; i < 5; i++ ) {
  5.               vectInt.push_back( i );
  6.               if ( 3 == i ) {
  7.                      vectInt.push_back( i );
  8.               }
  9.        }
  10.        vectInt.erase( remove( vectInt.begin(), vectInt.end(), 3 ), vectInt.end() );
  11.        cout << " after deleted , size = " << vectInt.size() << endl;
  12.        for ( i = 0; i < vectInt.size();; i++ ) {
  13.               cout << "i = " << i << " , " << vectInt[i] << endl;
  14.        }
  15. }
  16. 运行结果为:
  17. after deleted , size = 4 // 从这行可以看出,删除后容器的大小变化了
  18. i = 0 , 0
  19. i = 1 , 1
  20. i = 2 , 2
  21. i = 3 , 4
从结果可以看出来,所有值为3的元素确实被删除了。
对于Vector容器存放其他比较复杂的对象,就可以用remove_if()加函数对象的方法。

例7:
  1. #include <iostream>
  2. #include <sstream>
  3. #include <string>
  4. #include <vector>
  5. #include <algorithm>
  6. #include <list>
  7. using namespace std;
  8. class CTest
  9. {
  10.    public:
  11.       CTest(const string& str, int iPrice):m_strName(str),m_iPrice(iPrice){}
  12.       void vPrint(){ cout<<"name=" << m_strName << "price=" << m_iPrice <<endl;}
  13.    private:
  14.       string m_strName;
  15.       int m_iPrice;
  16.       // 由于两个函数对象要访问CTest类的Private成员,所以设为友员;
  17.       friend class CStrFunc;
  18.       friend class CIntFunc;
  19. };
  20. // 函数对象,根据string比较
  21. class CStrFunc
  22. {
  23.    string m_str;
  24. public:
  25.    CStrFunc(const string& str):m_str(str){ }
  26.    bool operator() (const CTest& left) { return (m_str == left.m_strName)?true:false; }
  27. };
  28. // 函数对象,根据int比较
  29. class CIntFunc
  30. {
  31.    int m_iPrice;
  32. public:
  33.    CIntFunc(int iPrice):m_iPrice(iPrice){ }
  34.    bool operator()(const CTest& left) { return (m_iPrice == left.m_iPrice)?true:false; }
  35. };
  36. void main()
  37. {
  38.    vector<CTest> vectTest;
  39.    int i;
  40.    for(i=0;i<5;++i)
  41.    {
  42.       stringstream stream; // 流格式化符,把int转化为string;
  43.       stream<<i;
  44.       string str = stream.str();
  45.       CTest dTest(str,i);
  46.       vectTest.push_back(dTest);
  47.     }
  48.     for (  i = 0 ; i < vectTest.size(); i++ )  
  49.     {
  50.        vectTest[ i ].vPrint();
  51.      }
  52.      // 删除所有m_strName = "3"的元素
  53.      vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CStrFunc( "3" ) ), vectTest.end() );
  54.      cout << "delete 3 after : " << endl;
  55.      for (  i = 0 ; i < vectTest.size(); i++ ) 
  56.     {
  57.            vectTest[ i ].vPrint();
  58.      }
  59.      //   删除所有m_iPrice = 2的元素
  60.      vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CIntFunc( 2 ) ),vectTest.end() );
  61.      cout << "delete 2 after : " << endl;
  62.       for (  i = 0 ; i < vectTest.size(); i++ )  
  63.       {
  64.             vectTest[ i ].vPrint();
  65.        }
  66. }

手工编写for循环代码删除STL序列式容器中元素的方法,使用STL中通用算法或容器成员函数删除元素的方法,两者之间的比较:

1.   前者代码重复。

2.   前者容易出错,不够清晰。

3.   效率:

0                  1           2       3                 2           5       6                 7

0                  1           3       2                 5           6       7

0                  1           3       5                 6           7

用第一种方法删除所有值为2的元素

从上图可以看出,每删除一个元素,后面的所有元素都到往前移动一位,导致一次内存大搬迁。



0                  1           2       3                 2           5       6                 7

0                  1           3       2                 5           6       6                 7

0                  1           3       5                 6           7



用第二种方法删除所有值为2的元素

从上面可以看出,删除时元素2被后面元素覆盖,不会到元素移位和内存大搬迁,残余数据留到末尾一次全部删除,也不会导致内存大搬迁,所以后者的方法要比前者在效率上好很多。







原创粉丝点击