4

这是我必须使用的常见代码模式:

class foo {
public:
    void InitMap();
    void InvokeMethodsInMap();
    static void abcMethod();
    static void defMethod();
private:
    typedef std::map<const char*, pMethod> TMyMap;
    TMyMap m_MyMap;
}

void
foo::InitMap()
{
    m_MyMap["abc"] = &foo::abcMethod;
    m_MyMap["def"] = &foo::defMethod;
}

void
foo::InvokeMethodsInMap()
{
    for (TMyMap::const_iterator it = m_MyMap.begin();
        it != m_MyMap.end(); it++)
    {
        (*it->second)(it->first);
    }
}

但是,我发现处理映射的顺序(在 for 循环中)可能会因构建配置是 Release 还是 Debug 而有所不同。似乎发布版本中发生的编译器优化会影响此顺序。

我认为通过begin()在上面的循环中使用,并在每次方法调用后递增迭代器,它将按初始化顺序处理映射。但是,我还记得读过地图是作为哈希表实现的,并且无法保证顺序。

这特别烦人,因为大多数单元测试都是在 Debug 构建上运行的,并且通常在外部 QA 团队开始测试之前不会发现奇怪的顺序依赖错误(因为他们使用的是 Release 构建)。

谁能解释这种奇怪的行为?

4

3 回答 3

16

不要const char*用作地图的键。这意味着映射是按字符串的地址排序的,而不是字符串的内容。改为使用 astd::string作为键类型。

std::map不是哈希表,它通常实现为红黑树,并且保证元素按某些标准排序(默认情况下,<键之间的比较)。

于 2008-09-26T01:30:41.910 回答
10

map的定义是:
map<Key, Data, Compare, Alloc>

最后两个模板参数也是默认的:
比较:less<Key>
Alloc:allocator<value_type>

将新值插入地图时。新值(valueToInsert)按顺序与旧值进行比较(注意这不是顺序搜索,标准保证最大插入复杂度为 O(log(N))),直到 Compare(value,ValueToInsert) 返回 true。因为您使用'const char*'作为键。比较对象正在使用less<const char*>此类只是对两个值执行 <。因此,实际上您是在比较指针值(而不是字符串),因此顺序是随机的(因为您不知道编译器会将字符串放在哪里。

有两种可能的解决方案:

  • 更改键的类型,以便它比较字符串值。
  • 定义另一个满足您需要的比较类型。

我个人(如 Chris)只会使用 std::string 因为字符串上使用的 < 运算符会返回基于字符串内容的比较。但是为了争论,我们可以只定义一个比较类型。

struct StringLess
{
    bool operator()(const char* const& left,const char* const& right) const
    {
        return strcmp(left,right) < 0;
    }
};

///

typedef std::map<const char*, int,StringLess> TMyMap;
于 2008-09-26T02:49:49.037 回答
3

如果你想使用 const char * 作为你的地图的键,还要设置一个键比较函数,使用 strcmp (或类似的)来比较键。这样,您的地图将按字符串的内容排序,而不是按字符串的指针值(即内存中的位置)。

于 2008-09-26T02:56:09.567 回答