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

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

 

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

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

map multimap )容器为关联式容器,是编程中经常使用的容器,有键值( 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;

       }

}

 

2

void main() {

 

       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;

       }

}

 

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 );

              }

       }

}

 

分析:

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

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

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

 

正确的方法:

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;

       }

}

 

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;

              }

 

       }

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

 

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

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

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

                   if (  itMap->second == 2 ) {

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

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

                            mapInt.erase( itMap++ );

                   }

                   else {

                            ++itMap;

                   }

         }

STL之顺序容器和关联容器总结

顺序容器          Vector中所采用的数据结构非常简单:线性连续空间。当分配空间被占满而仍然需要添加元素时,vector便会进行一场空间重新配置的大工程!在这里,程序员需要注意的是,一旦...
  • lhc548453346
  • lhc548453346
  • 2016年03月07日 21:02
  • 819

STL容器的删除元素问题

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

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

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

C++使用STL:慎重选择删除元素的方法

原文出自: C++使用STL:慎重选择删除元素的方法   关于要删除容器中元素要选用什么样的方法(因为不同的容器所支持或删除的方法效率不同),总结如下: 假设容器:Containerc; ...
  • iFuMI
  • iFuMI
  • 2016年05月03日 18:03
  • 1035

STL 顺序容器,关联容器

向量 vector :   是一个线性顺序结构。相当于数组,但其大小可以不预先指定,并且自动扩展。它可以像数组一样被操作,由于它的特性我们完全可以将vector 看作动态数组。 在创建一个ve...
  • gavin0123
  • gavin0123
  • 2014年08月18日 15:12
  • 1134

STL容器删除的正确打开方式

在应用中,我们通常不可避免地要对容器中的某些特定元素进行删除操作。这看起来并不是什么困难的问题。我们先写一个循环来迭代容器中的元素,如果迭代元素是要删除的元素,则删除之。代码如下所示(错误示范):ve...
  • Scythe666
  • Scythe666
  • 2016年04月30日 10:08
  • 923

C++ STL常用容器删除操作注意事項

C/C++程序员对STL 容器肯定不会陌生, 以下是对常用容器进行 erase 操作注意事项的总结:vectorvector 容器是用数组实现的, 它在内存是连续分布的.对它进行 insert 或 ...
  • u010090316
  • u010090316
  • 2017年04月19日 20:24
  • 293

STL-set (集合)之删除元素

set概述 和vector、list不同,set、map都是关联式容器。set内部是基于红黑树实现的。插入和删除操作效率较高,因为只需要修改相关指针而不用进行数据的移动。  在进行数据删除操作后,...
  • hurmishine
  • hurmishine
  • 2016年11月10日 21:25
  • 4455

c++ stl容器vector删除(erase),遍历等基本用法介绍及头文件

Vectors 包含着一系列连续存储的元素,其行为和数组类似。访问Vector中的任意元素或从末尾添加元素都可以在常量级时间复杂度内完成,而查找特定值的元素所处的位置或是在Vector中插入元素则是线...
  • Xwxcy
  • Xwxcy
  • 2016年01月21日 16:59
  • 805

【STL】关联式容器

【STL】常用容器总结,带有复习性质的学习更有效率;【STL】 关联式容器复习在复习关联式容器之前,先回忆一下底层的基本数据机构; 【树】: 常见的树有二叉树,搜索二叉树,RB_tree, AVL _...
  • bitboss
  • bitboss
  • 2017年06月28日 11:36
  • 155
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:STL关联式容器中删除元素的方法和陷阱四
举报原因:
原因补充:

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