`
canco
  • 浏览: 252403 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多

2.使用STL中通用算法或容器成员函数删除元素的方法

以上手工编写for循环代码删除容器中元素的方法也有一些问题,如果判断条件特别复杂,又有循环判断的话,循环中间又有异常处理的话,++itVect的位置就要小心放置了,稍不留意就要出错。所以手工编写代码删除容器中元素的方法不太安全,代码重复,也不够优雅,要注意的地方很多。

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

0        1        2        3         3       4  移除前<o:p></o:p>

0        1        2        4         3       4  移除后<o:p></o:p>

 <o:p></o:p>

移除值为3的元素。移除后3被其后的4替代,最后两位元素为残余数据。<o:p></o:p>

 <o:p></o:p>

5<o:p></o:p>

void main() {<o:p></o:p>

       vector<int> vectInt;<o:p></o:p>

       int i;<o:p></o:p>

       for (i = 0; i < 5; i++ ) {<o:p></o:p>

              vectInt.push_back( i );<o:p></o:p>

              if ( 3 == i ) {<o:p></o:p>

                     vectInt.push_back( i );<o:p></o:p>

              }<o:p></o:p>

       }<o:p></o:p>

       remove( vectInt.begin(), vectInt.end(), 3 );<o:p></o:p>

       cout << " after deleted , size = " << vectInt.size() << endl;<o:p></o:p>

       for ( i = 0; i < vectInt.size();; i++ ) {<o:p></o:p>

              cout << "i = " << i << " , " << vectInt[i] << endl;<o:p></o:p>

       }<o:p></o:p>

}<o:p></o:p>

运行结果为:<o:p></o:p>

after deleted , size = 6 // 从这行可以看出,移除后容器的大小没变<o:p></o:p>

i = 0 , 0<o:p></o:p>

i = 1 , 1<o:p></o:p>

i = 2 , 2<o:p></o:p>

i = 3 , 4 //  从这行可以看出:“应被删除的元素”3 被其后的“未被删除的元素”4覆盖<o:p></o:p>

i = 4 , 3<o:p></o:p>

i = 5 , 4     <o:p></o:p>

 <o:p></o:p>

所以要彻底删除还应该把后面的残余数据删除掉,这可以通过调用容器的成员函数erase()做到。<o:p></o:p>

6<o:p></o:p>

void main() {<o:p></o:p>

       vector<int> vectInt;<o:p></o:p>

       int i;<o:p></o:p>

       for (i = 0; i < 5; i++ ) {<o:p></o:p>

              vectInt.push_back( i );<o:p></o:p>

              if ( 3 == i ) {<o:p></o:p>

                     vectInt.push_back( i );<o:p></o:p>

              }<o:p></o:p>

       }<o:p></o:p>

       vectInt.erase( remove( vectInt.begin(), vectInt.end(), 3 ), vectInt.end() );<o:p></o:p>

       cout << " after deleted , size = " << vectInt.size() << endl;<o:p></o:p>

       for ( i = 0; i < vectInt.size();; i++ ) {<o:p></o:p>

              cout << "i = " << i << " , " << vectInt[i] << endl;<o:p></o:p>

       }<o:p></o:p>

}<o:p></o:p>

运行结果为:<o:p></o:p>

after deleted , size = 4 // 从这行可以看出,删除后容器的大小变化了<o:p></o:p>

i = 0 , 0<o:p></o:p>

i = 1 , 1<o:p></o:p>

i = 2 , 2<o:p></o:p>

i = 3 , 4<o:p></o:p>

从结果可以看出,所有值为3的元素确实被删除了。<o:p></o:p>

对于vector容器存放其他比较复杂的对象,就可以用remove_if()加函数对象(Function Object)的方法。<o:p></o:p>

如:<o:p></o:p>

7<o:p></o:p>

#include <iostream><o:p></o:p>

#include <sstream><o:p></o:p>

#include <string><o:p></o:p>

#include <vector><o:p></o:p>

#include <algorithm><o:p></o:p>

#include <list><o:p></o:p>

using namespace std;<o:p></o:p>

class CTest {<o:p></o:p>

public:<o:p></o:p>

       CTest( const string& str, int iPrice ) : m_strName( str ), m_iPrice( iPrice ) { }<o:p></o:p>

      void vPrint() { cout << "name=" << m_strName << " price = " << m_iPrice << endl;<o:p></o:p>

       }<o:p></o:p>

private:<o:p></o:p>

       string m_strName;<o:p></o:p>

       int   m_iPrice;<o:p></o:p>

       //     由于两个函数对象要访问CTest类的private成员,所以设为友员。<o:p></o:p>

       friend class CStrFunc;<o:p></o:p>

       friend class CIntFunc;<o:p></o:p>

};<o:p></o:p>

//     函数对象,根据string比较<o:p></o:p>

class CStrFunc {<o:p></o:p>

       string m_str;<o:p></o:p>

public:<o:p></o:p>

       CStrFunc( const string& str ) : m_str( str ) {<o:p></o:p>

       }<o:p></o:p>

       bool operator() ( const CTest& left ) {<o:p></o:p>

              return ( m_str == left.m_strName ) ? true : false;<o:p></o:p>

       }<o:p></o:p>

};<o:p></o:p>

//     函数对象,根据int比较<o:p></o:p>

class CIntFunc {<o:p></o:p>

       int m_iPrice;<o:p></o:p>

public:<o:p></o:p>

       CIntFunc( int iPrice ) : m_iPrice( iPrice ) {<o:p></o:p>

       }<o:p></o:p>

       bool operator() ( const CTest& left ) {<o:p></o:p>

              return ( m_iPrice == left.m_iPrice ) ? true : false;<o:p></o:p>

       }<o:p></o:p>

};<o:p></o:p>

void main( ) {<o:p></o:p>

 <o:p></o:p>

       vector< CTest > vectTest;<o:p></o:p>

       int i;<o:p></o:p>

       for (  i = 0; i < 5 ; i++ ) {<o:p></o:p>

              stringstream stream; //       流格式化符,把int转化为string<o:p></o:p>

              stream << i;<o:p></o:p>

              string str = stream.str();<o:p></o:p>

              CTest clTest( str, i );<o:p></o:p>

              vectTest.push_back( clTest );<o:p></o:p>

       }<o:p></o:p>

      for (  i = 0 ; i < vectTest.size(); i++ )  {<o:p></o:p>

              vectTest[ i ].vPrint();<o:p></o:p>

       }<o:p></o:p>

       //     删除所有m_strName = "3"的元素<o:p></o:p>

       vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CStrFunc( "3" ) ),<o:p></o:p>

              vectTest.end() );<o:p></o:p>

       cout << "delete 3 after : " << endl;<o:p></o:p>

      for (  i = 0 ; i < vectTest.size(); i++ )  {<o:p></o:p>

              vectTest[ i ].vPrint();<o:p></o:p>

       }<o:p></o:p>

       //     删除所有m_iPrice = 2的元素<o:p></o:p>

       vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CIntFunc( 2 ) ),<o:p></o:p>

              vectTest.end() );<o:p></o:p>

       cout << "delete 2 after : " << endl;<o:p></o:p>

      for (  i = 0 ; i < vectTest.size(); i++ )  {<o:p></o:p>

              vectTest[ i ].vPrint();<o:p></o:p>

       }<o:p></o:p>

}<o:p></o:p>

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

1.   前者代码重复。<o:p></o:p>

2.   前者容易出错,不够清晰。<o:p></o:p>

3.   效率:<o:p></o:p>

0                  1           2       3                 2           5       6                 7<o:p></o:p>

0                  1           3       2                 5           6       7<o:p></o:p>

0                  1           3       5                 6           7<o:p></o:p>

 用第一种方法删除所有值为2的元素<o:p></o:p>

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

 <o:p></o:p>

0                  1           2       3                 2           5       6                 7<o:p></o:p>

0                  1           3       2                 5           6       6                 7<o:p></o:p>

0                  1           3       5                 6           7<o:p></o:p>

 <o:p></o:p>

 用第二种方法删除所有值为2的元素<o:p></o:p>

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

分享到:
评论

相关推荐

    STL关联容器入门

    STL关联容器入门

    stl_set容器详细使用方法

    Set 容器中元素的类型可以是任何类型,包括内置类型和用户定义类型。元素类型必须满足以下条件: * 元素类型必须支持小于运算符(&lt;),用于比较元素的大小。 * 元素类型必须支持复制构造函数和赋值运算符,用于元素...

    STL -容器,string容器

    STL容器可以分为两大类:序列式容器和关联式容器。 序列式容器的特点是强调值的排序,每个元素均有固定的位置。常见的序列式容器有vector、list、deque等。 关联式容器的特点是无需强调元素的物理顺序,各元素之间...

    SGI STL 关联式容器源码

    关联式容器是SGI STL中的一类重要数据结构,它们允许通过键(key)来高效地查找、插入和删除元素。在本压缩包中,我们很可能会找到如`set`、`multiset`、`map`和`multimap`等关联式容器的源代码。 关联式容器的核心...

    C++ STL vector 容器介绍

    在STL中,`vector`是一种非常重要的容器,它是一个动态数组,允许在任意位置进行元素的插入和删除,并能保持元素的顺序。 `vector`容器的主要特点包括: 1. 动态数组:`vector`的底层实现是一个动态数组,这意味着...

    关联式容器序列式容器对比

    这些容器不像序列式容器(如vector、list)那样有明显的头部和尾部,也不提供push_back、push_front、pop_back、pop_front等操作。相反,关联式容器主要提供了对元素基于键值的快速查找、插入和删除功能,这得益于...

    c++/STL容器设计相关

    STL容器提供了一系列操作方法,包括构造、赋值、插入、删除、查找、容量管理等。例如: - 构造函数:初始化容器,可以指定初始容量、拷贝构造等。 - 插入操作:如push_back()(向向量尾部添加元素)、insert()(在...

    C++之STL标准库容器成员一览表.pdf

    STL容器都提供了一些基本的成员变量和成员函数,用于对容器中的元素进行操作。这些成员变量和成员函数包括: * size():返回容器中的元素个数。 * max_size():返回容器的最大容量。 * empty():检查容器是否为空。...

    STL标准模板库vector容器

    容器是用于存储数据的对象,STL提供的容器包括了序列式容器和关联式容器。序列式容器,如vector、list等,强调元素的顺序性,即容器中的每个元素都拥有一个固定的位置。关联式容器,如set、map等,以元素间的关联...

    STL容器和算法函数表

    STL不仅提供容器,还有一系列通用算法,如`sort`, `find`, `copy`, `transform`等,这些算法可以作用于任何满足一定要求的序列上,极大提高了编程效率和代码的可读性。例如: - `sort`: 对容器进行排序。 - `find`: ...

    STL常用容器1

    在本文中,我们将重点讨论STL中的容器,特别是序列式容器和关联式容器。 1. 序列式容器: 序列式容器是一类按照元素插入顺序存储的容器,包括vector、queue、deque、priority_queue、list和stack。这些容器有着...

    STL_vector容器介绍

    **容器**:在C++ STL中,容器是用来存储和组织其他对象的数据结构。`std::vector`是一个容器,能够像容器一样存放各种类型的对象。 **动态数组**:`std::vector`能够根据需要动态调整大小,这意味着当需要更多的...

    C++_STL之set容器使用方法

    在C++标准模板库(STL)中,`set`容器是一种非常重要的关联容器,主要用于存储唯一元素,并且这些元素会根据其键值自动排序。`set`内部通常采用红黑树(一种自平衡的二叉查找树)来实现,这使得它在执行插入、删除和...

    STL容器使用代码

    在STL中,容器是一类能够存储数据的对象,包括vector、string、deque、queue、list、set、multiset、map和multimap。下面将详细介绍这些容器的使用和API方法。 1. **vector**:动态数组,可以自动扩展其大小。常用...

    STL 定义的具体容器类

    STL定义的三类容器——顺序性容器、关联式容器和容器适配器,各自拥有独特的优势和适用场景。了解并掌握这些容器的特点和使用方法对于有效地进行C++程序开发至关重要。通过合理选择合适的容器类型,可以大大提高代码...

    C++实战篇:STL-容器

    C++实战篇:STL-容器 C++实战篇:STL-容器 C++实战篇:STL-容器 C++实战篇:STL-容器 C++实战篇:STL-容器 C++实战篇:STL-容器 C++实战篇:STL-容器 C++实战篇:STL-容器 C++实战篇:STL-容器 C++实战篇:STL-容器 ...

    C++STL容器.md

    本文将详细介绍C++ STL中两种主要类型的容器:序列式容器与关联式容器。 #### 二、序列式容器 序列式容器是基于元素在内存中连续或接近连续存储的特点设计的,主要包括vector、deque、list等几种类型。 ##### 1. ...

    STL的容器deque的使用

    `deque`(双端队列)是C++标准模板库(STL)中的一种重要容器,它提供了类似数组的功能,同时支持在两端进行高效插入和删除操作。与vector相比,deque在两端操作时通常具有更好的性能,因为它不需要移动大量元素。...

    STL各种使用方法实例

    本实例将详细讲解STL中常见的容器和算法的使用方法,帮助初学者深入理解并掌握STL的强大功能。 首先,我们来看STL中的容器: 1. **Map**: Map是一种关联容器,它按照键值(key)进行排序,并存储键值对(key-value...

    STL Guide STL详细教程

    容器是STL(Standard Template Library)中的一个核心概念,它指的是用于存储其他对象(即其元素)的对象,并提供一系列方法来访问这些元素。所有符合容器模型的类型都有一种相关的迭代器类型,可以用来遍历容器中的...

Global site tag (gtag.js) - Google Analytics