Consider the code below:

#include 
#include 
#include 

using std::string;

struct string_hashfunc {
    size_t operator()(const string *a) const
    {
        size_t hash = 0;
        if(a == NULL) return 0;
        for (string::const_iterator it = a->begin(); it != a->end(); it++) {
            hash = hash * (*it);
        }
    }
};

typedef __gnu_cxx::hash_map StringHash;

int main(void) {
    StringHash sh;
    string *a = new string("earth");
    string *b = new string("sky");
    sh[a] = b;
    for (StringHash::iterator it = sh.begin(); it != sh.end(); it++) {
        delete it->first;
        delete it->second;
    }
}

It could be compiled and run on CentOS 5 (gcc-4.1.2), but will core dump at runtime.

g++ test.cpp -o test -g2 -O0
./test
Segmentation fault

The gdb stack shows the breakpoint is in string_hashfunc::operator():

(gdb) bt
#0  0x00007ffff7b7c1e3 in std::basic_string, std::allocator >::end() const () from /usr/lib64/libstdc++.so.6
#1  0x0000000000400fd0 in string_hashfunc::operator() (this=0x7fffffffe861, a=0x606620) at test.cpp:12
......

Let’s see the source code of “ext/hash_map” in /usr/include/c++/4.1.2/ext/hashtable.h:

  template                                                                 
    _Hashtable_iterator<_Val, _Key, _HF, _ExK, _EqK, _All>&                        
    _Hashtable_iterator<_Val, _Key, _HF, _ExK, _EqK, _All>::                       
    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);                       
      while (!_M_cur && ++__bucket < _M_ht->_M_buckets.size())                     
        _M_cur = _M_ht->_M_buckets[__bucket];                                      
    }                                                                              
      return *this;                                                                
    }

And in the implementation of _M_bkt_num():

      size_type                                                                    
      _M_bkt_num_key(const key_type& __key) const                                  
      { return _M_bkt_num_key(__key, _M_buckets.size()); }                         
                                                                                   
      size_type                                                                    
      _M_bkt_num(const value_type& __obj) const                                    
      { return _M_bkt_num_key(_M_get_key(__obj)); }                                
                                                                                   
      size_type                                                                    
      _M_bkt_num_key(const key_type& __key, size_t __n) const                      
      { return _M_hash(__key) % __n; } 

It use _M_hash() to compute the bucket number of the key, and the _M_hash() is actually string_hashfunc::operator(). The reason is clear now: the iterator want to increase, so it call operator++() –> _M_bkt_num() –> _M_bkt_num_key() –> _M_hash() –> string_hashfunc::operator() and it can’t fetch the key because it has been freed in “delete it->first”.

How about new g++ and new c++ library? Let’s try to write the same program on CentOS 7 (gcc-4.8.5) and change “ext/hash_map” to “unordered_map” (for c++ 11 standard):

#include 
#include 
#include 

using std::string;

struct string_hashfunc {
    size_t operator()(const string *a) const
    {
        size_t hash = 0;
        if(a == NULL) return 0;
        for (string::const_iterator it = a->begin(); it != a->end(); it++) {
            hash = hash * (*it);
        }
    }
};

typedef std::unordered_map StringHash;

int main(void) {
    StringHash sh;
    string *a = new string("earth");
    string *b = new string("sky");
    sh[a] = b;
    for (StringHash::iterator it = sh.begin(); it != sh.end(); it++) {
        delete it->first; 
        delete it->second;
    }        
}

Then build it:

g++ test.cpp -o test -g2 -O0 -std=c++11
./test

Everything goes normal because the new implementation of c++ library use “_M_nxt” to point to the next hash node instead of using hash function (could see it in /usr/include/c++/4.8.5/bits/hashtable_policy.h).