__gnu_cxx::hash_map死循环一例

/// 一个hash_map死循环的例子: class obj { public: obj(char *_name) { strncpy(name, _name, 31); } char name[32]; /// anyothers }; hash_map list; typedef hash_map::iterator hash_iter; obj *pObj = new obj("testUnion"); if(pObj) { list.insert(pObj->name, pObj); } strncpy(pObj->name, "hahaha", 31); /// 这里就会死循环 for(hash_iter it = list.begin(); it != list.end(); ++it) { /// ... } /// 原因,见__gnu_cxx::hash_map的源代码,一句一句分析 /// for循环执行第一句hash_iter it = list.begin(); 这里调用了函数hash_map::begin(): /// __gnu_cxx::hash_map中begin()定义如下 iterator begin() { return _M_ht.begin(); }; /// 我们看下_M_ht是什么东东,__gnu_cxx::hash_map有一个typedef typedef hashtable, _Key, _HashFcn, _Select1st >, _EqualKey, _Alloc> _Ht; _Ht _M_ht; /// 也就是说_M_ht是一个hashtable类型,那么_M_ht.begin()调用也就是hashtable::begin(): iterator hashtable::begin() { for(size_type __n = 0; __n < _M_buckets.size(); ++__n) if(_M_buckets[__n]) return iterator(_M_buckets[__n], this); return end(); } /// _M_buckets的定义在hashtable中是std::vector<_Node*, _Nodeptr_Alloc>, /// 这也就是所谓的hash表,就是一个vector,而_Node的定义是_Hashtable_node, /// 也就是一个hash的结点,也就是说vector中存放了一个指针,指向一个结点 template struct _Hashtable_node { _Hashtable_node *_M_next; /// 解决hash碰撞用的链表 _Val _M_val; /// 真正的hash值value }; /* 所以begin函数就是循环hash表,找到第一个存在的元素,返回一个迭代器,迭代器是_Hashtable_iterator类型,其中有两个指针,第一个_M_cur指向的是hash表vector中当前的一个元素,第二个_M_ht指向了这个hash表本身。返回这个迭代器,会得到一个_M_ht指向当前hash表,_M_cur指向第一个元素的迭代器。如果hash表中没有元素,返回一个_M_cur为空的迭代器,也就是end();,接着我们的程序,下一个执行的语句应该是it != list.end(),这个比较简单,也就是看迭代器it的_M_cur指针是不是为空,在我们的程序里一定是不为空的,所以下面要执行循环体,循环体执行完成后,会执行++it,问题的关键就在这个++it,我们看下__gnu_cxx的源码,_Hashtable_iterator::operator++(): */ _Hashtable_iterator& _Hashtable_iterator::operator++() { const _Node* __old = _M_cur; _M_cur = _M_cur->_M_next; /// 先在链表中找,如果有碰撞情况,会找到 if(!_M_cur) { size_type __bucket = _M_ht->_M_bkt_num(__old->_M_val); /// 这句代码意思是使用当前迭代器指向的hash元素的关键字值得到一个hash值 while(!_M_cur && ++__bucket < _M_ht->_M_buckets.size()) _M_cur = _M_ht->_M_buckets[__bucket]; } return *this; } /* 这段程序的意思是找到当前迭代器指向元素的下一个元素,_M_bkt_num(__old->_M_val);这个函数调用传入的是当前迭代器指向的hash元素的关键字的值,而返回的是使用参数,利用hashtable的hash函数,也就是传入的模板参数_HashFcn,得到一个hash的key值,这里就出了问题,原因是这样的,我们在之前程序中insert到hash表时,那个obj对象的关键字name是“testUnion”,我们使用的hash函数得到的key是98,而在程序中我们有一句strncpy(pObj->name, "hahaha", 31);,这样,name就是"hahaha",我们使用的hash函数得到有key是34,而在程序执行到_M_ht->_M_bkt_num(__old->_M_val),我们的name已经变成了"hahaha",这个函数返回的值是34,而__gnu_cxx::hashtable并没有检查_M_ht->_M_buckets[34]是否为空指针,程序自信的以为这个是OK的,就开始找34之后的“第二个元素”,找到__bucket为98时,“第二个元素”出现了,但98其实是第一个元素,而现在_M_ht->_M_buckets[__bucket]的指针指向的地址和++it函数调用前_M_cur指向的地址是一样的,经过很多次循环后,其实程序做了一次_M_cur = _M_cur操作,那么下一次++it也只会执行相同的操作,也就没有结束的那一天了,于是死循环就出现了 解决方法很简单,strncpy(pObj->name, "hahaha", 31);之前,先把之前在hash表中的元素删除,再改名,再插入hash表就可以了,或者让gnu改改代码?这个不可能了,呵呵 */ 

你可能感兴趣的:(其它)