C++的map::erase

这篇博客探讨了在C++中使用map删除元素时遇到的问题。在Windows环境下,两种迭代器删除方法都能正常工作,但在Linux下,直接通过erase返回的迭代器进行更新会导致错误。文章提醒开发者注意map的erase函数在不同平台的行为差异,并引用了STL标准文档说明返回iterator的erase方法并不常见。

产品代码在map中循环删除元素通常有两种方法:

举例:

  • iterator自加
        std::map<unsigned int, CFsMP4HeadFile*>::iterator buffer_ite = buffer_file_heads_mgmt_.begin();
        for ( ; buffer_ite != buffer_file_heads_mgmt_.end(); )
        {
    
            delete buffer_ite->second;
            buffer_ite->second = NULL;
            buffer_file_heads_mgmt_.erase(buffer_ite++);
        }
    
  • 获取下一个元素
        std::map<unsigned int, CFsMP4HeadFile*>::iterator buffer_ite = buffer_file_heads_mgmt_.begin();
        for ( ; buffer_ite != buffer_file_heads_mgmt_.end(); )
        {
    
            delete buffer_ite->second;
            buffer_ite->second = NULL;
            buffer_ite = buffer_file_heads_mgmt_.erase(buffer_ite);
        }
    

以上两种方法在windows下都没有问题,但在linux下第二种方法无法使用;
不管在linux还是Windows平台下,vector,list都有方法iterator erase( iterator _Where ),但是在Linux下map中没有方法iterator erase( iterator _Where );

返回iterator的erase貌似是不符合STL标准的:http://www.cplusplus.com/reference/map/map/erase/ 其中并没有提供上述版本的erase()

参考:http://www.cppblog.com/bujiwu/archive/2009/11/01/99928.aspx?opt=admin

<think>我们在讨论C++ STL中map::erase的原理。根据引用内容,map的内部实现通常是红黑树(一种自平衡二叉查找树)。当调用erase删除一个元素时,map会执行以下步骤: 1. 定位要删除的节点:通过给定的键值或迭代器找到对应的树节点。 2. 删除节点:从树中移除该节点,并调整树的结构以保持平衡(红黑树的特性要求删除后可能需要进行颜色调整和旋转操作)。 3. 释放内存:删除节点对象并释放内存。 4. 迭代器处理:被删除的节点对应的迭代器会失效(成为野指针),而其他迭代器仍然有效。根据SGI文档(引用[1]),maperase操作不会使其他指向现有元素的迭代器失效。 引用[1]中提到:“map的insert/erase操作都必须保证map的这样一个特性:这些操作不影响其它指向已存在元素的迭代器。” 因此,只有被删除元素的迭代器会失效。 在代码实现上,map::erase有两种常见的用法(引用[3]): - 利用后置自增:`mapTest.erase(iter++);` 在删除前将迭代器指向下一个元素(因为iter++会返回当前值,然后自增)。 - 使用erase的返回值:`iter = mapTest.erase(iter);` erase返回被删除元素的下一个元素的迭代器。 注意:在C++11标准中,map::erase会返回下一个元素的迭代器,而C++98中map::erase没有返回值。 引用[4]强调了vector和maperase操作上的区别:vector的erase会导致被删除元素之后的所有迭代器失效(因为元素需要向前移动),而map(红黑树)只影响被删除元素的迭代器。 因此,map::erase的原理可以总结为: - 基于红黑树的节点删除操作,包括查找、删除和再平衡。 - 仅使指向被删除元素的迭代器失效,其他迭代器不受影响。 - 在C++11及以上,erase返回被删除元素的下一个元素的迭代器,以便安全地继续遍历。 §§相关问题§§ 1. 红黑树在删除节点后如何进行再平衡? 2. 为什么vector的erase操作会导致后续所有迭代器失效,而map不会? 3. C++11中map::erase的返回值是如何实现的? </think> ### `map::erase` 的原理分析 在 C++ STL 中,`map` 是基于**红黑树**(自平衡二叉查找树)实现的关联容器,其 `erase` 操作的核心原理如下: #### 1. **内部结构特性** - `map` 的底层是红黑树,每个节点存储键值对(`pair<const Key, T>`),并通过树结构维护有序性。 - 红黑树通过颜色约束(红/黑节点)和旋转操作保证插入/删除后仍近似平衡,时间复杂度为 $O(\log n)$。 #### 2. **`erase` 操作步骤** 当调用 `map::erase(iterator pos)` 时: 1. **定位节点**:根据迭代器 `pos` 找到待删除节点。 2. **删除节点**: - 若节点为叶节点:直接删除。 - 若节点有子节点:用前驱或后继节点替换(保持有序性),再删除被替换节点。 3. **再平衡**:触发红黑树的再平衡算法(旋转+颜色调整),确保树满足: - 根节点为黑色。 - 红色节点的子节点必须为黑色。 - 任意路径的黑节点数相同。 4. **更新迭代器**: - 被删除节点的迭代器立即失效。 - **其他迭代器不受影响**(引用[1])。 #### 3. **迭代器失效规则** - **仅当前迭代器失效**:因红黑树节点独立存储,删除仅影响目标节点(引用[1][4])。 ```cpp auto it = myMap.find(key); myMap.erase(it); // it 失效,其他迭代器安全 ``` - **安全遍历方法**(引用[3]): ```cpp for (auto it = myMap.begin(); it != myMap.end(); ) { if (condition) it = myMap.erase(it); // C++11 返回下一迭代器 else ++it; } // 或使用后置自增(兼容旧标准) // myMap.erase(it++); ``` #### 4. **与 `vector::erase` 的关键区别** | 特性 | `map::erase` | `vector::erase` | |---------------------|-------------------------------|-------------------------------| | **数据结构** | 红黑树(节点离散存储) | 动态数组(连续内存) | | **迭代器失效范围** | 仅被删迭代器失效 | 被删位置之后所有迭代器失效 [^4] | | **时间复杂度** | $O(\log n)$(平衡树操作) | $O(n)$(需移动后续元素) | | **内存影响** | 仅释放目标节点 | 可能触发整体内存重分配 [^1] | #### 5. **设计哲学** - **稳定性保障**:`map` 的插入/删除不破坏其他元素的迭代器(引用[1]),这是红黑树的核心优势。 - **未定义行为规避**:若未正确处理失效迭代器(如 `vector` 删除后继续使用旧迭代器),会导致未定义行为(引用[2])。 > 示例:删除所有偶数值的键 > ```cpp > std::map<int, std::string> myMap = {{1, "a"}, {2, "b"}, {3, "c"}}; > for (auto it = myMap.begin(); it != myMap.end(); ) { > if (it->first % 2 == 0) it = myMap.erase(it); // 安全删除 > else ++it; > } > // 结果: myMap = {{1, "a"}, {3, "c"}} > ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值