【问题标题】:std::map iteration - order differences between Debug and Release buildsstd::map 迭代 - 调试和发布版本之间的顺序差异
【发布时间】:2008-09-26 01:29:20
【问题描述】:

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

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(),并在每次方法调用后递增迭代器,它将按初始化顺序处理映射。但是,我还记得读过,map 是作为哈希表实现的,并且无法保证顺序。

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

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

【问题讨论】:

    标签: c++ stl


    【解决方案1】:

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

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

    【讨论】:

    • 当我像上面那样使用 for() 循环时,我不确定使用 const char* 作为地图的键会如何影响遍历顺序。我们是否按照字符串地址的顺序遍历地图?那么 m_MyMap.begin() 会去内存地址最低的字符串索引的入口吗?
    • 是的,正如 Chris 指出的那样,它是通过内存地址进行的。字符串文字的内存地址由编译器确定,因此它们可以是任何东西。对于代码中相同字符串的两次出现,它们甚至可能不同。
    • 如前所述,提供谓词也有效。使用 const char* 可以具有保存字符串副本的额外优势。
    • 只有在内存要求非常非常严格的情况下才采用自定义比较器路径,否则代码清晰度的下降不太值得。
    【解决方案2】:

    map的定义是:
    map

    最后两个模板参数也是默认的:
    比较:少
    分配:        分配器

    将新值插入地图时。新值 (valueToInsert) 按顺序与旧值进行比较 (N.B. 这不是顺序搜索,标准保证最大插入复杂度为 O(log(N)) ) 直到 Compare(value, ValueToInsert) 返回真。因为您使用 'const char*' 作为键。比较对象正在使用 less 此类只是对两个值执行

    有两种可能的解决方案:

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

    我个人(如 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;
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-09
        • 2011-02-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多