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).