在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;
}
}
<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>
}
分享到:
相关推荐
在这份文件中,列出了书籍中的若干要点,涵盖了容器的选择、容器无关代码的误解、对象拷贝的轻量正确性、容器操作的优化、指针管理、线程安全性、关联容器的使用、迭代器的高效运用、STL算法的正确使用方法、仿函数...
`multiset`是STL中的一种关联容器,它类似于`set`,但允许插入重复元素。`multiset`内部使用红黑树(Red-Black Tree)作为底层数据结构,保证了插入、删除和查找操作的时间复杂度为O(log n)。与`set`不同的是,当...
《Effective STL》、《STL源码剖析》、《STL标准程序库》以及英文版的《The C++ Standard Library》这四本书是C++程序员深入理解和高效利用STL(Standard Template Library,标准模板库)的必备参考资料。STL是C++...
其中,包括了对容器、迭代器、算法和函数对象的最佳实践,如正确理解和使用迭代器失效,何时选择栈、队列或关联容器,以及如何有效地利用STL的算法来优化代码性能。这本书还强调了STL模板特化的概念,以及如何利用...
`set`和`map`是基于红黑树的关联容器,分别按排序顺序存储元素和键值对。 2. **迭代器**:迭代器是STL的核心概念,它像指针一样指向容器中的元素,但具有更多操作,如递增、递减、比较和解引用。不同类型的容器有...
2. 迭代器:迭代器是STL的桥梁,它像指针一样指向容器中的元素,但具有更多操作,如前向、反向移动和访问元素。迭代器使得算法可以独立于容器类型,实现通用性。 3. 算法:STL提供了一套强大的算法库,包括排序、...
- `map`和`multimap`:关联容器,以键值对形式存储数据,自动排序。 3. **迭代器**:迭代器是STL中访问容器内元素的主要方式,有输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器五种类型,每种...
在`map`中,插入、删除和查找元素的复杂度通常是O(log n)。虽然`map`在插入和删除时不会像`vector`那样移动元素,但仍然需要注意迭代器在操作后的有效性。特别是当删除某个键值对时,与该键值对相关的迭代器将不再...
例如,vector提供随机访问,list支持快速插入和删除,而set和map则实现了关联容器,可以进行键值对的查找和操作。 2. 迭代器:迭代器是访问容器中元素的接口,它类似于指针,但更安全且功能更强大。迭代器允许你...
STL是C++编程中的核心部分,包含容器、迭代器、算法和函数对象等重要组件,能够帮助程序员构建高效、可维护的代码。这本书由50个独立但相互关联的规则组成,每个规则都旨在提高STL的使用效率和正确性。 1. **容器的...
deque支持两端操作,而set和map是基于红黑树的关联容器,提供键值对的存储和查找。 2. **迭代器**:STL的核心概念之一,它像指针一样指向容器中的元素,但具有更丰富的操作,如前向迭代、双向迭代和随机访问。理解...
- 容器的内存管理方式,例如动态增长机制(例如vector)和关联容器(如set和map)的内部实现。 - 迭代器的概念,它是STL的主要接口,允许程序以统一的方式访问各种容器。 - 算法的通用性和效率,它们通常不改变容器...
2. **容器的理解与选择**:STL提供了多种容器,如vector(动态数组)、list(双向链表)、deque(双端队列)、set(红黑树实现的集合)和map(关联容器)。理解它们的内部实现和性能特性,能帮助我们根据需求选择最...
4. **关联容器**:`set`和`map`是关联容器,它们都按照特定顺序(通常是升序)存储元素。`set`存储的是唯一元素,而`map`则将每个元素映射到一个值。`multiset`和`multimap`允许有重复的键值。 5. **迭代器**:迭代...
STL是C++标准库中的一个重要部分,它包括了容器(如vector、list、set等)、迭代器、算法和函数对象等组件,极大地提高了C++的效率和可复用性。在《Effective STL》中,Meyers通过一系列的“条目”(items)来阐述...
set和map是基于红黑树的关联容器,用于存储唯一元素,并提供按特定顺序访问的能力。 3. **迭代器**:迭代器是STL的关键概念,它像指针一样遍历容器中的元素,但具有更丰富的操作和更强的类型安全。迭代器有不同种类...
2. **容器**:STL的容器是存储元素的模板类,如vector(动态数组)、deque(双端队列)、list(双向链表)、set和map(红黑树实现的关联容器)。书中讲解了如何选择合适的容器以及如何有效地操作它们。 3. **迭代器...
2. 迭代器:迭代器是STL中的一种设计模式,它像指针一样可以指向容器中的元素,但功能更加强大,支持前向、双向和随机访问。迭代器提供了类似指针的语法,但具有更多操作,如递增、递减、比较和访问元素。 3. 预算...
"详细解说STL hash_map系列 -- STLDetailHashMap.htm"对STL中的关联容器hash_map进行了详细阐述。hash_map提供了基于哈希表的键值对存储,允许快速查找,这在需要高效查找的应用中非常有用。 "STL使用入门( Using ...