C++的标准库提供的 map类是保存 <key, value>的键-值对的容器, 操作符'[]'提供了类似于数组随机访问的语义, 可以通过key来方便地访问或者修改与之对应的value.

      另外, STL map作为容器也提供了对应的迭代器 map<key-type, value-type>::iterator, 使我们能够方便的通过iterator遍历map的键-值对成员.

      STL map提供的上述接口可以大大简化我们代码的数据结构已经代码量, 但是对于不熟悉STL map使用方法或者不了解STL map的实现原理的开发人员来说, 常常会因为不正确的使用导致非预期的问题, 本人就在实际的工作当中遇到过一些问题, 这也是写在这里的原因。

 

1. operator[]的语义导致的问题

定义 map<string, mystruct*> map_sample, mystruct为自定义类型。

     通过操作符[], 我们可以像数组一样访问map中的成员, 非常的方便, 如:

     mystruct *ptr = map_sample["wjb"];

     当键wjb存在不会导致问题,但是当键 'wjb'在当前map_sample中不存在时,操作符[]会自动在map_sample中添加键wjb,并且将对应的值初始化缺省值(这里将导致值被初始化为NULL)。这个特性在某些应用场景中可能会带来问题,如果值mystruct*每次都是malloc,这样在另外任务中通过[]访问后直接使用,碰到上面这种不存在情况时将导致访问空指针或者在调用free时导致程序core dump。看到这里你会说,那是因为你使用[]后没有判断指针是否为NULL导致的,不是[]的问题,但是成员'wjb'是实实在在的被添加了进去了,在你发现返回指针为NULL时,是不是要再去erase掉这个值为空的键值对呢?--吃力不讨好

     所以如果你的程序不能确定某个key是不是存在,建议不要使用操作符'[]', 而是通过使用接口find判断后再访问,可以避免出现上述问题。

 

2. map iterator的使用问题

     先看下面代码片断:

     std::map<int,int>::iterator it = m_map.begin();    //行1
     for(;it != m_map.end(); it++)                                 //行2
     {                                                                            //行3
         m_map.erase(it);                                              //行4
      }

     上面的代码会导致程序内存访问问题,导致程序core dump。

     STL map的内部实现为RB树,因此<key, value>对在内部作为树的一个节点出现的,节点间是通过类似于next,prev这样的指针关联起来的。因此在行4 erase掉it对应的键值队后,it指向的内存已经不属于这个map对象,随时可能被OS回收,而行2 中的it++操作又依赖于it的next指针,这是就出现了内存访问问题,你的程序可能会core,如果it指向的这块内存已经被回收。我们可以通过下面的方式解决这个问题

      std::map<int,int>::iterator it = m_map.begin();    //行1
     for(;it != m_map.end(); m_map.erase(it++))         //行2
     {

     }   

STL map 在调用erase之后,迭代器就失效了,在使用时时刻提醒自己。

 

本人是菜鸟,只是把记录的问题记下来,有可能说得不对,呵呵。

ps:本人的第一篇博文,哇哈哈~~~~~~~~~~~~

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐