std :: map iteration - 调试和发布版本之间的顺序差异

时间:2008-09-26 01:29:21

标签: c++ stl

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

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);
    }
}

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

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

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

任何人都可以解释这种奇怪的行为吗?

3 个答案:

答案 0 :(得分:16)

请勿使用const char*作为地图的关键字。这意味着地图按字符串的地址排序,而不是字符串的内容。而是使用std::string作为密钥类型。

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

答案 1 :(得分:10)

地图的定义是:
地图&lt;密钥,数据,比较,分配&gt;

最后两个模板参数的默认值:
比较:少&LT;钥匙及GT;
的Alloc:分配器&LT; VALUE_TYPE&GT;

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

有两种可能的解决方案:

  • 更改密钥的类型,以便比较字符串值。
  • 定义另一种能够满足您需求的比较类型。

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

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;

答案 2 :(得分:3)

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