【问题标题】:insertions into c++ map STL container is failing插入 c++ map STL 容器失败
【发布时间】:2011-04-11 21:34:33
【问题描述】:

请查看以下代码以更好地理解我的问题。

class compareByValue {                                                                             
  public:                                                                                          
    bool operator()(const string* s1, const string* s2) const                                      
    {                                                                                              
      if (s1 == s2)                                                                                
        return true;                                                                               
      if ((s1==NULL) || (s2==NULL))                                                                
        return false;                                                                              
      return (0 == s1->compare(s2->c_str()));                                                      
    }                                                                                                                                                                                             
}; 

map<string*, string*, compareByValue> nodeIdToIpAddress;

for (int i = 0; i < nrec; ++i) {
  nodeIdToIpAddress[ptr1[i]] = ptr2[i];                                              
  cout << "Added " << *(ptr1[i]) << " , " << *(ptr2[i]) << endl;
}

cout << "map has " << nodeIdToIpAddress.size() << " elements!" << endl;

我有一个映射,它维护键值对,它们是指向字符串对象的指针。我确定键和值都不是 NULL 指针。当我运行上面的程序时(好吧,我跳过了周围的代码以便更容易理解),“Added ... ...”被打印了 49 次。 ptr1[i], ptr2[i] 是指向字符串对象的指针,不是 NULL 指针,因为我的程序没有段错误。

我遇到的问题是,当我最后打印地图的大小时,它说地图中只有 1 个元素。

如果有人可以指导我找到解决方法,我将不胜感激。提前致谢。

编辑:@Mark 解决方案对我来说很有魅力。谢谢

EDIT2:在看到来自@Mark 和@James 的宝贵反馈后,我认为我不需要在我的地图中存储指向字符串的指针。我将更改我的代码以将字符串存储为键/值,这意味着我不需要自定义比较器仿函数。非常感谢。

【问题讨论】:

  • 我还不知道答案,但你能用return *s1 == *s2; 替换compare 行吗?
  • 我确实尝试过这个选项。它只打印一个元素。
  • 哇...compareByValue::operator() 的实现保证了 DailyWTF 的发布。 o.O

标签: c++ stl map


【解决方案1】:

您的比较器错误:if (s1 == s2)必须返回false,并且比较器必须产生strict weak ordering

[对于它的价值,使用指针类型作为std::map 的键充其量是不寻常的。]

【讨论】:

  • 如果容器中出现过空值,空值检查也是错误的:它不遵守严格的弱排序。
  • 此外,他似乎想要的是默认行为,因此该问题的解决方案是完全删除仿函数。但可能还有其他问题……
  • 我不相信,但做出你建议的改变并没有任何影响。
  • @Srikanth:你没抓住重点。詹姆斯陈述的事实必须遵守。这是必需的。您的仿函数没有遵循标准容器比较器的要求,即假设满足的容器,并且每次使用它时都调用了未定义行为。 Mark 的回答直接将你带到了一段优秀的代码,但他没有教你你做错了什么。
  • 此外,使用这种更复杂的方法,即使您得到正确的内存管理,您仍然可能执行同样多的动态分配(如果不是更多),因为您有两个动态分配:一个用于std::string 本身和 std::string 内部的一个作为其缓冲区。如果您有一个移动感知标准库(最新版本的 libstdc++、Visual C++ 2010 等),您将会做更多的工作。除非您已经分析了这部分代码并确定它实际上是性能瓶颈并且使用指针确实会提高性能,否则我会避免使用指针
【解决方案2】:

您的比较运算符不正确。只要您可以假设没有添加任何空值,如果检测到任何空值,您就可以抛出:

bool operator()(const string* s1, const string* s2) const                                      
{                                                                                              
  if (!s1 || !s2) throw std::runtime_error("Whatever");

  return *s1 < *s2;
}                                                                                 

编辑:我应该详细说明这里的推理。传入std::map(或setsort 例如)的比较操作必须满足严格的弱排序。这基本上意味着x OP xfalse,如果x OP ytrue 那么y OP xfalse,并且x OP yy OP z 暗示x OP z(以及关于项目的其他规则无法比较)。

您的函数在几个方面不满足这一点:if (s1 == s2) 需要返回 false。 if ((s1==NULL) || (s2==NULL)) 不能返回 false,因为它暗示 null &lt; anythinganything &lt; null。您必须将其拆分并为s1 null 返回true,为s2 null 返回false。 return (0 == s1-&gt;compare(s2-&gt;c_str())); 不正确,因为它是三向结果而不是 &lt; 关系。

由于您指定永远不会插入空值,因此最简单的解决方案似乎是在这种情况下抛出异常(如果其中一个为空),然后使用字符串内置运算符。

现在说了这么多,除非您有实际证据证明这是性能瓶颈,否则我强烈考虑不要在容器中按指针存储字符串。更简单的设计更容易维护和保持正确,这往往会带来更好的长期程序。

【讨论】:

  • 一记耳光。我怎么忘记了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-07
  • 1970-01-01
  • 2011-02-07
  • 2013-03-24
  • 2012-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多