2

我正在跟踪一个错误,我遇到了非常奇怪的行为。我有一组指针,当我一个接一个地擦除它们时,第一个会擦除,但是擦除另一个会导致我出现段错误。我用

   size_type erase( const key_type& key );

所以它不可能是迭代器。我的调试器在调用堆栈中向我展示了这一点:

0 - std::less<cSubscriber *>::operator() //cSubscriber is an abstract base class and I have a set of cSubscriber *

1 - std::_Rb_tree<cSubscriber*, cSubscriber*, std::_Identity<cSubscriber*>, std::less<cSubscriber*>, std::allocator<cSubscriber*> >::equal_range

2 -  std::_Rb_tree<cSubscriber*, cSubscriber*, std::_Identity<cSubscriber*>, std::less<cSubscriber*>, std::allocator<cSubscriber*> >::erase

3 - std::set<cSubscriber*, std::less<cSubscriber*>, std::allocator<cSubscriber*> >::erase

4 - cEventSystem::unsubscribe //my function, it is as follows in the class which has the set as its member

cEventSystem::unsubscribe(cSubscriber * ptr)
{
   set.erase(ptr);
}

在基础 cSubscriber 抽象类中有虚拟析构函数:

  virtual ~cSubscriber()
  {
      eventSystem.unsubscribe(this);
  }

有任何想法吗?我不知道它如何导致段错误,当没有这样的元素时,擦除应该只返回 0。或者当试图从空容器中删除某些东西时它可能会崩溃?(当添加 3 个不同的指针后集合的大小仅为 2 时,我有另一个错误,但这是另一回事)。

4

1 回答 1

5

如果您将无效地址传递给您std::set<SOMETHING*>::erase(),则在尝试将传递的值与容器中的值进行比较时,它将出现段错误。

例如:

struct IntPtrComparer {
    bool operator()(int* a, int* b) const {
        return *a < *b;
    }
};

std::set<int*,IntPtrComparer> a;
a.insert(new int);
a.erase(NULL);

更新

根据评论

由于您没有重新定义默认比较器,并且默认比较器不会取消引用您的指针,因此唯一的方法是您std::set的已损坏。

在内部,std::set实现为二叉树。这意味着它有很多指针可以找到一个值并删除它。如果std::set损坏,其中一些指针将指向无效的内存地址。这个无效的内存地址将用于将比较值的引用(指针的引用)传递给。接收对指针的引用,并将取消引用该引用以获取指针值。cSubscriber* & conststd::lessstd::less

这样,内部的无效内存std::set只显示在std::less,因为std::set实际上并没有触及无效内存,它把无效内存地址给了我们可怜的家伙std::less打开它并在它的脸上遇到了错误。

我的观点是,如果您创建一个使用副本而不是引用的比较器,那么std::set当它尝试复制指针值以提供给比较器时,损坏将出现在内部。

于 2013-02-06T18:19:28.553 回答