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

STL关联式容器中删除元素的方法和陷阱 四

阅读更多

STL(标准模板库)中经常会碰到要删除容器中部分元素的情况,本人在编程中就经常编写这方面的代码,在编码和测试过程中发现在STL中删除容器有很多陷阱,网上也有不少网友提到如何在STL中安全删除元素这些问题。

上一篇文章主要讨论序列式容器vectorlist中安全删除元素的方法和可能会遇到的陷阱,这一次讨论在mapmultimap)容器中如何安全的删除和插入元素。

mapmultimap)容器为关联式容器,是编程中经常使用的容器,有键值(key)和实值(value),又称字典、映射表。

你能看出以下代码有什么问题?

1

#pragma warning (disable : 4786)

#include <iostream>

#include <map>

using namespace std;

void main() {

       map< int, int* > mapInt;

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

              mapInt[ i ] = new int( i );

       }

       //     再插入键值为2的元素

       mapInt[ 2  ] = new int( 2 );

      

       //     做一些操作

       //     删除内存。

       map< int, int* >::iterator itMap = mapInt.begin();

       for ( ; itMap != mapInt.end();  ++itMap ) {

              delete itMap->second;

       }

}

 <o:p></o:p>

2

void main() {

 <o:p></o:p>

       map< int, int* > mapInt;

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

              mapInt.insert( make_pair( i, new int( i ) ) );

       }

       //     再插入键值为2的元素

       mapInt.insert( make_pair( 2, new int( 2 ) ) );

      

       //     做一些操作

       //     删除内存。

       map< int, int* >::iterator itMap = mapInt.begin();

       for ( ; itMap != mapInt.end();  ++itMap ) {

              delete itMap->second;

       }

}

 <o:p></o:p>

3

void main() {

       map< int, int > mapInt;

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

              mapInt.insert( make_pair( i,  i ) );

       }

      

       mapInt.insert( make_pair( 5,  2 ) );

      

       //     删除所有实值为2的元素

       map< int, int >::iterator itMap = mapInt.begin();

       for ( ; itMap != mapInt.end();  ++itMap ) {

              if (  itMap->second == 2 ) {

                     mapInt.erase( itMap );

              }

       }

}

 <o:p></o:p>

分析:

       1将导致内存泄露,因为mapInt[ 2  ] = new int( 2 );这条语句把原来键值为2的元素的实值指针覆盖了,原来的指针就成为野指针,导致内存泄露。

2也将导致内存泄露,因为mapInt.insert( make_pair( 2, new int( 2 ) ) );这条语句因为键值为2的元素已经存在,导致插入元素失败,所以指向刚分配的内存的指针成为野指针,导致内存泄露。

map容器插入元素的方法。可以调用map容器的insert成员函数插入元素,或者直接用map容器的下标运算式赋值,但这里有一个地方要注意,如果实值为指针的话,插入重复键值的元素时,会导致内存泄露。所以对于插入元素时,必须检查是否插入成功。

 <o:p></o:p>

正确的方法:

void main() {

       map< int, int* > mapInt;

       bool bRet;

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

              int* pI = new int( i );

              bRet = mapInt.insert( make_pair( i, pI ) ).second;

              if ( !bRet ) {

                     //       插入元素不成功。

                     delete pI;

              }

       }

       //     再插入键值为2的元素

       int* pI = new int( 2 );

       bRet = mapInt.insert( make_pair( 2, pI ) ).second;

       if ( !bRet ) {

              //       插入元素不成功。

              delete pI;

       }

      

       //     做一些操作

       //     删除内存。

       map< int, int* >::iterator itMap = mapInt.begin();

       for ( ; itMap != mapInt.end();  ++itMap ) {

              delete itMap->second;

       }

}

 <o:p></o:p>

3将导致程序未定义的错误,在windows中即是访问非法内存,程序当掉。因为mapInt.erase( itMap );调用后itMap迭代器已无效了,所以当执行++itMap时,访问非法内存,导致程序当掉。

如果erase()总是返回下一元素的位置,那就可以像在vector容器中删除元素一样,如:

//     删除所有实值为2的元素

       map< int, int >::iterator itMap = mapInt.begin();

       for ( ; itMap != mapInt.end();   ) {

              if (  itMap->second == 2 ) {

                     itMap = mapInt.erase( itMap );

              }

              else {

                     ++itMap;

              }

 <o:p></o:p>

       }

但是,注意,以上的方式只在vc使用P.J.STL中才能编译通过,而使用SGI STL库则编译不过,因为SGISTL库在设计中考虑到如果用户不需要这一特性,就会损失性能,因此否决了这种想法。所以要保证可移植性,最好采用下面的方法:<o:p></o:p>

 <o:p></o:p>

//       删除所有实值为2的元素<o:p></o:p>

         map< int, int >::iterator itMap = mapInt.begin();<o:p></o:p>

         for ( ; itMap != mapInt.end();  ) {<o:p></o:p>

                   if (  itMap->second == 2 ) {<o:p></o:p>

                            //         itMap++将使itMap指向下一个元素,但返回原迭代器的副本,所以<o:p></o:p>

                            //         erase()被调用时,itMap不指向将要被删除的元素<o:p></o:p>

                            mapInt.erase( itMap++ ); <o:p></o:p>

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

                   else {<o:p></o:p>

                            ++itMap;<o:p></o:p>

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

         }

分享到:
评论

相关推荐

    effective stl 中文 pdf

    在这份文件中,列出了书籍中的若干要点,涵盖了容器的选择、容器无关代码的误解、对象拷贝的轻量正确性、容器操作的优化、指针管理、线程安全性、关联容器的使用、迭代器的高效运用、STL算法的正确使用方法、仿函数...

    STL容器multiset的使用

    `multiset`是STL中的一种关联容器,它类似于`set`,但允许插入重复元素。`multiset`内部使用红黑树(Red-Black Tree)作为底层数据结构,保证了插入、删除和查找操作的时间复杂度为O(log n)。与`set`不同的是,当...

    Effectiv STL, STL源码剖析, STL标准程式库,The C++ standard library

    《Effective STL》、《STL源码剖析》、《STL标准程序库》以及英文版的《The C++ Standard Library》这四本书是C++程序员深入理解和高效利用STL(Standard Template Library,标准模板库)的必备参考资料。STL是C++...

    STL 教程和帮助手册

    其中,包括了对容器、迭代器、算法和函数对象的最佳实践,如正确理解和使用迭代器失效,何时选择栈、队列或关联容器,以及如何有效地利用STL的算法来优化代码性能。这本书还强调了STL模板特化的概念,以及如何利用...

    Effective.STL中文

    `set`和`map`是基于红黑树的关联容器,分别按排序顺序存储元素和键值对。 2. **迭代器**:迭代器是STL的核心概念,它像指针一样指向容器中的元素,但具有更多操作,如递增、递减、比较和解引用。不同类型的容器有...

    STL学习必备:Effective-STL,STL_Programmer_Guider,TheC++StandardLibrary打包下载

    2. 迭代器:迭代器是STL的桥梁,它像指针一样指向容器中的元素,但具有更多操作,如前向、反向移动和访问元素。迭代器使得算法可以独立于容器类型,实现通用性。 3. 算法:STL提供了一套强大的算法库,包括排序、...

    Eff_STL_CN.pdf

    - `map`和`multimap`:关联容器,以键值对形式存储数据,自动排序。 3. **迭代器**:迭代器是STL中访问容器内元素的主要方式,有输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器五种类型,每种...

    STL使用注意问题

    在`map`中,插入、删除和查找元素的复杂度通常是O(log n)。虽然`map`在插入和删除时不会像`vector`那样移动元素,但仍然需要注意迭代器在操作后的有效性。特别是当删除某个键值对时,与该键值对相关的迭代器将不再...

    C++STL目前可能最全的书籍和代码

    例如,vector提供随机访问,list支持快速插入和删除,而set和map则实现了关联容器,可以进行键值对的查找和操作。 2. 迭代器:迭代器是访问容器中元素的接口,它类似于指针,但更安全且功能更强大。迭代器允许你...

    Effective STL中文版

    STL是C++编程中的核心部分,包含容器、迭代器、算法和函数对象等重要组件,能够帮助程序员构建高效、可维护的代码。这本书由50个独立但相互关联的规则组成,每个规则都旨在提高STL的使用效率和正确性。 1. **容器的...

    efficient STL

    deque支持两端操作,而set和map是基于红黑树的关联容器,提供键值对的存储和查找。 2. **迭代器**:STL的核心概念之一,它像指针一样指向容器中的元素,但具有更丰富的操作,如前向迭代、双向迭代和随机访问。理解...

    c++STL资料pdf汇总

    - 容器的内存管理方式,例如动态增长机制(例如vector)和关联容器(如set和map)的内部实现。 - 迭代器的概念,它是STL的主要接口,允许程序以统一的方式访问各种容器。 - 算法的通用性和效率,它们通常不改变容器...

    Effective STL

    2. **容器的理解与选择**:STL提供了多种容器,如vector(动态数组)、list(双向链表)、deque(双端队列)、set(红黑树实现的集合)和map(关联容器)。理解它们的内部实现和性能特性,能帮助我们根据需求选择最...

    Effective.STL中文.rar

    4. **关联容器**:`set`和`map`是关联容器,它们都按照特定顺序(通常是升序)存储元素。`set`存储的是唯一元素,而`map`则将每个元素映射到一个值。`multiset`和`multimap`允许有重复的键值。 5. **迭代器**:迭代...

    EffectiveSTL_STL_C++_experience_gas841_C++STL_

    STL是C++标准库中的一个重要部分,它包括了容器(如vector、list、set等)、迭代器、算法和函数对象等组件,极大地提高了C++的效率和可复用性。在《Effective STL》中,Meyers通过一系列的“条目”(items)来阐述...

    Effective_STL.rar_Effective-STL_effective STL

    set和map是基于红黑树的关联容器,用于存储唯一元素,并提供按特定顺序访问的能力。 3. **迭代器**:迭代器是STL的关键概念,它像指针一样遍历容器中的元素,但具有更丰富的操作和更强的类型安全。迭代器有不同种类...

    effectivestl_effectiveSTL_清晰;带目录;中英版全_

    2. **容器**:STL的容器是存储元素的模板类,如vector(动态数组)、deque(双端队列)、list(双向链表)、set和map(红黑树实现的关联容器)。书中讲解了如何选择合适的容器以及如何有效地操作它们。 3. **迭代器...

    C++高手之STL学习资料

    2. 迭代器:迭代器是STL中的一种设计模式,它像指针一样可以指向容器中的元素,但功能更加强大,支持前向、双向和随机访问。迭代器提供了类似指针的语法,但具有更多操作,如递增、递减、比较和访问元素。 3. 预算...

    stl资料合集

    "详细解说STL hash_map系列 -- STLDetailHashMap.htm"对STL中的关联容器hash_map进行了详细阐述。hash_map提供了基于哈希表的键值对存储,允许快速查找,这在需要高效查找的应用中非常有用。 "STL使用入门( Using ...

Global site tag (gtag.js) - Google Analytics