【问题标题】:Determining whether an object is in a std::set确定对象是否在 std::set 中
【发布时间】:2009-05-04 16:40:27
【问题描述】:

我正在尝试确定一个对象是否已包含在 std::set 中。根据 msdn(和其他来源),如果 set::find 函数没有找到您要求的元素,它应该返回 end()

但是,当我实现如下代码时,set::find 会返回垃圾 (0xbaadf00d)。

set<Cell*> cellSet;

Cell* cell = new Cell();    

if (cellSet.find(cell) == cellSet.end())
{
    ...
}

我使用正确吗?我正在使用 Visual C++ 2005。

【问题讨论】:

    标签: c++ stl set


    【解决方案1】:

    您发布的代码将始终执行if 内的代码,而0xbaadf00d 实现的“one-past-the-end”标记。

    【讨论】:

    • 是的,显然是这样,显然崩溃来自其他地方,因为在移动了一些东西之后,它现在似乎工作正常。
    • 它可发音且几乎有意义的事实表明它存在故意的不良价值。出于这个原因,0xdeadbeef 作为规范的错误值或未初始化值而广受欢迎。
    • 你不是说发布的代码应该总是执行if中的代码吗? cellSet 是空的(在发布的代码中没有执行任何插入操作)并且单元格是新的(即使执行了插入操作也不可能在 cellSet 中),因此 cellSet.find(cell) 应该始终返回 cellSet。结尾()。只是想清楚。
    • @Naaff:在原始帖子中,中间有一个 cellSet.insert(cell) 。我会调整我的答案。
    【解决方案2】:

    在使用 stl set 时,我喜欢使用 count 函数来确定成员资格。我认为这使代码更易于阅读。

    set<Cell*> cellSet;
    
    Cell* cell = new Cell();    
    
    if (cellSet.count(cell) == 0)
    {
        ...
    }
    

    【讨论】:

    • 很好的方法,为什么我没有想到呢? :)
    • 调用 count() 需要检查每个元素,而 find() 仅在元素不存在时检查每个元素。所以 find() 会更快。如果可读性是一个问题,最好编写一个内联函数 is_member,它接受一个关联容器和一个元素并返回 find() == end()。
    • @Mark Ruzon:对于 std::set,两者都具有对数复杂度。事实上,对于 GNU stdlibc++,count 的实现基本上是: { return find(x) == end() ? 0:1; } gcc.gnu.org/viewcvs/trunk/libstdc%2B%2B-v3/include/bits/…
    • Mark 所说的对于多重集来说是正确的,但这是一个集。所以我想这取决于所讨论的代码是否在编写时着眼于超越集合的泛化。
    • 我相信 Scott Meyers 在 Effective STL 中也推荐了这种方法
    【解决方案3】:

    callSet.end() 的值是否也为 0xbaadf00d?

    编辑

    我在 VS2008 中运行了这个示例代码,一切都按预期运行。 find 函数返回一个指向原始值的迭代器。

    您究竟看到了什么行为?它是返回 end() 还是返回集合中的另一个位置?

    【讨论】:

    • 确实如此,但如果我尝试在它们之间执行 == 则会崩溃,而且我无论如何都不能依赖该值,因为它只是一个调试标志。在发布模式下,它们将包含两个随机值。
    • 如果找到对象,则返回正确的迭代器,如果没有,则返回指向 0xbaadf00d 的迭代器
    • @Kevin,只要 end() == 0xbaadf00d 这应该是正确的行为。
    • @Kevin: 等一下——当 find() 没有找到任何东西时,你还在用 find() 的返回值做些什么吗?
    • @j_random_hacker:不,我没有用它做任何事情,而是将它与结束进行比较。事实证明我的代码运行良好,而崩溃来自其他地方。
    【解决方案4】:

    尝试编译和运行只是您提供的代码 sn-p,我保证您会发现它执行没有问题。这个问题几乎可以肯定是由于程序中其他地方出现的内存分配错误,例如引用未初始化的指针或指向 deleted 的对象的指针。

    您是否熟悉 C++ 容器如何管理其对象?他们不会为您删除指针。在可能的情况下,使用对象容器而不是指向对象的指针总是更安全。 (在某些情况下,指针容器是必要的——尤其是当您希望容器存储来自单个类层次结构的不同类型的对象时。)

    【讨论】:

      【解决方案5】:

      一个简单的错误是您应该测试不等于 end。

      set<Cell*> cellSet;
      Cell* cell = new Cell();
      if (cellSet.find(cell) != cellSet.end())     // Test NOT EQUAL to end
      {
           // Found item in set.
      }
      

      但您也应该注意,您不是在比较实际的 Cell 值,而是比较指向 Cell 对象的指针(这可能是您想要的,也可能不是您想要的)。通常在 C++ 中,您不倾向于将指针存储在容器中,因为指针没有隐含的所有权,但有时可以。

      要实际比较对象,您需要使用 find_if() 并传递谓词(函子)。

      struct PointerCellTest
      {
          Cell&  m_lhs;
          PointerCellTest(Cell* lhs): m_lhs(lhs) {}
          bool operator()(Cell* rhs)
          {
               return lhs.<PLOP> == rhs.<PLOP>
          }
      };
      
      
      if(find_if(cellSet.begin(),cellSet.end(),PointerCellTest(cell)) != cellSet.end())
      {
           // Found item in set.
      }
      

      【讨论】:

      • 在这种情况下,该组的使用非常临时,所以应该没问题。如果它是一个长期的事情,它会被设置为。我不知道 find_if,所以谢谢!下次我会记住的。
      猜你喜欢
      • 2020-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 2020-09-25
      • 2011-12-12
      • 2010-12-14
      相关资源
      最近更新 更多