STL序列式容器中删除元素的方法和陷阱三

转载 2006年06月15日 13:27:00

3 list 容器中删除元素的方法

对于 list 容器,由于 list 本身有 remove remove_if 的成员函数,所以最好优先考虑 list 自己的算法,对于 remove 函数,比较简单,不再讨论,对于 remove_if 函数,本人发现在 vc6.0 中有重大问题。我试了多种函数对象,总是编译不过,通过查看源代码,才发现 VC6.0 中对 remove_if() 函数作了简化,只提供了一种比较函数,它只能删除不等于某值的元素, VC6.0 remove_if() 函数的源码如下:

typedef binder2nd<not_equal_to<_Ty> > _Pr1;

       void remove_if(_Pr1 _Pr)

              {iterator _L = end();

              for (iterator _F = begin(); _F != _L; )

                     if (_Pr(*_F))

                            erase(_F++);

                     else

                            ++_F; }

从源码中可以看出, remove_if _Pr1 函数对象被固定为 binder2nd<not_equal_to<_Ty> > 一种格式。而在 VC7.0 中已经修改了这个 bug ,源码如下:

template<class _Pr1>

              void remove_if(_Pr1 _Pred)

              {     // erase each element satisfying _Pr1

              iterator _Last = end();

              for (iterator _First = begin(); _First != _Last; )

                     if (_Pred(*_First))

                            erase(_First++);

                     else

                            ++_First;

              }

VC7.0 remove_if()是成员模板函数,可以用任何判断条件的函数对象。

例如:

8:

#include <iostream>

#include <string>

#include <list>

#include <algorithm>

using namespace std;

class CTest{

public:

       CTest( int i ) : m_iPrice ( i ) {     }

       int operator == ( const CTest& right ) const{

              return ( m_iPrice == right.m_iPrice ) ? 1 : 0;

       }

       int operator != ( const CTest& right ) const{

              return ( m_iPrice != right.m_iPrice ) ? 1 : 0;

       }

       int operator < ( const CTest& right ) const {

              return ( m_iPrice < right.m_iPrice ) ? 1 : 0;

       }

private:

       int m_iPrice;

       friend class CTestFunc;

};

class CTestFunc {            //       函数对象

public:

       int m_value;

       CTestFunc( int i ) : m_value( i ) {}

       bool operator () ( const CTest& clFirst ) {

              return ( clFirst.m_iPrice == m_value ) ? true : false;     }

};

void main() {

       list< CTest > listTest;

       for ( int i = 0; i < 5; i++ ) {

              CTest clTest( i );

              listTest.push_back( clTest );

       }

       cout << "remove before : " << listTest.size() << endl;

//     删除所有为 2 的元素

       listTest.remove_if( CTestFunc( 2 )  ); //       这条语句在 vc6.0 中不能编译通过 ,VC7.0 中可以

       cout << "remove after : 2, size =   " << listTest.size() << endl;

 

       //     删除所以不等于 2 的元素, VC6.0 中只能以这种方式调用 remove_if() 函数

       listTest.remove_if(  bind2nd( not_equal_to<CTest>(), 2 )    );

       cout << "remove after not equal to 2, size =  " << listTest.size() << endl;

 

       //     因为 CTest 类提供了 == < 成员函数,所以也可以用 remove 函数

       listTest.remove( 2 ); //       删除所有为 2 的元素

       cout << "remove after : 2, size =  " << listTest.size() << endl;

}

不知道在 VC6.0 中能否突破只能函数对象被固定为 binder2nd<not_equal_to<_Ty> > 一种格式的限制?欢迎诸位大虾不吝赐教。不过采用通用算法 remove_if 只是多了几次对象的赋值的负担,如果对象不是太大,用通用算法的性能也是可以接受的。

另外,这些天使用了 VC7.0 后,感觉非常棒,不仅几乎符合 Standard C++ 规范,错误提示也更清晰,而编译速度和编译后的文件大小大大减小,如我原来的一个大量使用了模板的程序,用 VC6.0 编译后 Release 版的可执行文件大小为 1.2M ,用 VC7.0 编译后只有 420K ,我想可能 VC7.0 在代码优化和模板代码的膨胀等方面有了极大的改善;在 STL 的实现上也有了极大的改进,把原来的一些效率不好的地方都改进了,处理策略基本与 SGI STL 一致。

4 STL 容器中元素为指针情况下的删除方法

对于容器中的元素为指针的删除方法。如果容器中的元素为指针则不能用上面介绍的用通过算法或成员函数的方法删除元素,因为那样做会导致内存泄露,容器中的元素为指针指向的内存没有释放,在这种情况下有以下方法解决:

1.   尽可能不用指针作为容器的元素。

2.   如果是因为要减少对象拷贝和赋值方面的负担,而要在容器中存放指针的话,可以考虑用 boost 库中的智能指针 shared_ptr 包装指针,达到容器中引用的语意。

3.   如果你不希望因为使用 boost::shared_ptr 增加引用计数的负担,认为引入智能指针不好理解,那么你用指针作为容器的元素要千万小心,这时你要自己管理内存。

例如:    

9 :用 boost 库中的智能指针 shared_ptr 包装指针的例子:

#include <iostream>

#include <sstream>

#include <string>

#include <vector>

#include <algorithm>

#include <list>

#include <boost/smart_ptr.hpp> // 要包含 BOOST 类库中智能指针的头文件

using namespace std;

class CTest {

public:

       CTest( const string& str, int iPrice ) : m_strName( str ), m_iPrice( iPrice ) { }

      void vPrint() { cout << "name=" << m_strName << " price = " << m_iPrice << endl;

       }

private:

       string m_strName;

       int   m_iPrice;

       friend class CStrFunc;

       friend class CIntFunc;

};

//     函数对象,根据 string 比较

class CStrFunc {

       string m_str;

public:

       CStrFunc( const string& str ) : m_str( str ) {

       }

       //     此处要改为 boost::shared_ptr<CTest>& ,因为 vector 容器中的元素为

//       boost::shared_ptr<CTest>

       bool operator() ( const boost::shared_ptr<CTest>& left ) {

              return ( m_str == (*left).m_strName ) ? true : false;

       }

};

//     函数对象,根据 int 比较

class CIntFunc {

       int m_iPrice;

public:

       CIntFunc( int iPrice ) : m_iPrice( iPrice ) {

       }

//     此处要改为 boost::shared_ptr<CTest>& ,因为 vector 容器中的元素为

//       boost::shared_ptr<CTest>

       bool operator() ( const boost::shared_ptr<CTest>& left ) {

              return ( m_iPrice == (*left).m_iPrice ) ? true : false;

       }

};

void main( ) {

 

       vector< boost::shared_ptr<CTest>  > vectTest;

       int i;

       for (  i = 0; i < 5 ; i++ ) {

              stringstream stream;

              stream << i;

              string str = stream.str();

              boost::shared_ptr<CTest>  ptrShare( new CTest( str, i ) );

              vectTest.push_back( ptrShare );

       }

      for (  i = 0 ; i < vectTest.size(); i++ )  {

              ( *vectTest[ i ] ).vPrint();

       }

       //     删除所有 m_strName = "3" 的元素

       vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CStrFunc( "3" ) ),

              vectTest.end() );

       cout << "delete 3 after : " << endl;

      for (  i = 0 ; i < vectTest.size(); i++ )  {

              ( *vectTest[ i ] ).vPrint();

       }

       //     删除所有 m_iPrice = 2 的元素

       vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CIntFunc( 2 ) ),

              vectTest.end() );

       cout << "delete 2 after : " << endl;

      for (  i = 0 ; i < vectTest.size(); i++ )  {

              ( *vectTest[ i ] ).vPrint();

       }

}

以上代码不会导致内存泄露。

10 :自己编程删除容器中元素为指针的例子:

 

#include <iostream>

#include <sstream>

#include <string>

#include <vector>

#include <algorithm>

using namespace std;

class CTest {

public:

       CTest( const string& str, int iPrice ) : m_strName( str ), m_iPrice( iPrice ) { }

      void vPrint() { cout << "name=" << m_strName << " price = " << m_iPrice << endl;

       }

private:

       string m_strName;

       int   m_iPrice;

       //     声明友员函数,因为 vDeleteVector 函数要访问 CTest private 成员变量

       friend void vDeleteVector( vector< CTest*  >& vectTest, const string& str );

       friend void vDeleteVector( vector< CTest*  >& vectTest, int iPrice );

      

 

};

//     根据 CTest 类中 m_strName 比较

void vDeleteVector( vector< CTest*  >& vectTest, const string& str ) {

       vector< CTest* >::iterator itVect = vectTest.begin();

       for ( ; itVect != vectTest.end();; ) {

              if ( (*itVect)->m_strName == str ) {

                     //       删除 vector 容器中指针元素指向的内容,防止内存泄露

                     delete *itVect;

                     itVect = vectTest.erase( itVect );

              }

              else {

                     ++itVect;

              }

       }

 

}

//     根据 CTest 类中 m_iPrice 比较

void vDeleteVector( vector< CTest*  >& vectTest, int iPrice ) {

       vector< CTest* >::iterator itVect = vectTest.begin();

       for ( ; itVect != vectTest.end(); ) {

              if ( (*itVect)->m_iPrice == iPrice ) {

                     //       删除 vector 容器中指针元素指向的内容,防止内存泄露

                     delete *itVect;

                     itVect = vectTest.erase( itVect );

              }

              else {

                     ++itVect;

              }

       }

 

}

 

void main( ) {

 

       vector< CTest*  > vectTest;

       int i;

       for (  i = 0; i < 5 ; i++ ) {

              stringstream stream;

              stream << i;

              string str = stream.str();

              CTest* pclTest =  new CTest( str, i ) ;

              vectTest.push_back( pclTest );

       }

      for (  i = 0 ; i < vectTest.size(); i++ )  {

              vectTest[ i ]->vPrint();

       }

       //     删除所有 m_strName = "5" 的元素

       vDeleteVector( vectTest, "3" );

       cout << "delete 3 after : " << endl;

      for (  i = 0 ; i < vectTest.size(); i++ )  {

              vectTest[ i ]->vPrint();

       }

       //     删除所有 m_iPrice = 2 的元素

       vDeleteVector( vectTest, 2 );

       cout << "delete 2 after : " << endl;

      for (  i = 0 ; i < vectTest.size(); i++ )  {

              vectTest[ i ]->vPrint();

       }

}

原则:

1.   尽可能用通用算法。相信 STL 的算法要比自己的实现高效、优雅、安全。

2.   优先用容器自身的成员函数。 见《 Effective STL 》中 Item 44: Prefer member functions to algorithms with the same names

3.   尽可能熟悉函数对象。

4.   多看 STL 的源码,了解其实作。

5.   不成熟的优化是一切恶果的根源。编写代码,安全第一。

综上,在 STL 中删除容器中部分元素时要特别小心,不过通过使用通用算法或容器本身的删除函数,能大大减小重复代码和程序出错的机会,能够使代码得到优化,生成高效的代码。

STL容器的删除元素问题

STL的容器分为两类,一类是序列式容器,即数据顺序连续存储,如:vector、deque;另一类是关联式容器,即数据不连续存储,如:map、list、set。对于STL容器的数据删除操作,有一些需要注...
  • shudou
  • shudou
  • 2013年12月15日 19:59
  • 2471

stl容器在循环遍历中删除元素的方式

在实际的应用中,会碰到需要遍历容器中的元素,并且在判断某些的条件后,将某个元素从容器中清除的场景: #include #include #include #include #inclu...
  • jwybobo2007
  • jwybobo2007
  • 2013年03月12日 15:17
  • 2592

C++ STL set容器常用用法

set是STL中一种标准关联容器。它底层使用平衡的搜索树——红黑树实现,插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,所以效率比较高。set,顾名思义是“集合”的意思,在set中...
  • CerberuX
  • CerberuX
  • 2016年06月28日 10:39
  • 6060

STL序列式指针容器中删除元素的方法

在STL(标准模板库)中经常会碰到要删除容器中部分元素的情况,本人在编程中就经常编写这方面的代码,在编码和测试过程中发现在STL中删除容器有很多陷阱,网上也有不少网友提到如何在STL中安全删除元素这些...
  • shuaiqidiudiu
  • shuaiqidiudiu
  • 2013年06月17日 09:46
  • 950

STL:循环删除容器中元素的方法和陷阱

算法大师Donald Knuth:不成熟的优化是一切恶果的根源(Permature optimization is the root of all evil )。   STL中的容器主要是两种:序...
  • wang37921
  • wang37921
  • 2011年12月16日 15:07
  • 553

STL容器删除元素的陷阱

今天看Scott Meyers大师的stl的用法,看到了我前段时间犯的一个错误,发现我写的代码和他提到错误代码几乎一模一样,有关stl容器删除元素的问题,错误的代码如下: std::vector m...
  • w52770567
  • w52770567
  • 2012年04月09日 10:49
  • 196

循环删除容器中元素的方法和陷阱

算法大师Donald Knuth:不成熟的优化是一切恶果的根源(Permature optimization is the root of all evil )。 STL中的容器主要是两种:序列式容...
  • Road2010
  • Road2010
  • 2013年05月17日 10:45
  • 470

C++ STL容器遍历删除元素的方法

C++ STL容器遍历删除元素的方法  上周在做内存池回收的时候,遍历deque(使用了双端队列来管理内存块)的每一个元素,查找满足条件的内存块并进行回收,结果竟然coredump了。   写了个简...
  • tinywolfcgl
  • tinywolfcgl
  • 2016年12月05日 11:49
  • 309

总结STL中容器中元素的删除方法

首先看下面的关于删除容器中元素的例子(VS2010中编写):  #include "stdafx.h" #include #include #include #include #include...
  • BBOOT
  • BBOOT
  • 2015年03月22日 15:22
  • 788

c++中在顺序容器中访问、插入、删除元素的操作

c++中的顺序容器有vector,deque,queue,list,stack,priority_queue等 访问顺序容器内元素的操作: c.back( ) 返回容器c的最后一个元素的引用。...
  • YoungStar70
  • YoungStar70
  • 2016年12月10日 15:21
  • 279
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:STL序列式容器中删除元素的方法和陷阱三
举报原因:
原因补充:

(最多只允许输入30个字)